github-issue-tower-defence-management 2.19.1 → 2.19.2

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 (780) hide show
  1. package/bin/adapter/entry-points/cli/index.js +0 -0
  2. package/package.json +5 -1
  3. package/.env.example +0 -7
  4. package/.gitattributes +0 -2
  5. package/.github/CODEOWNERS +0 -6
  6. package/.github/copilot-instructions.md +0 -6
  7. package/.github/dependabot.yml +0 -19
  8. package/.github/instructions/code-review.instructions.md +0 -50
  9. package/.github/workflows/api-created_issue_pr.yml +0 -97
  10. package/.github/workflows/ci-failure-issue-creator.yml +0 -48
  11. package/.github/workflows/close-manual-prs.yml +0 -80
  12. package/.github/workflows/codeql.yml +0 -107
  13. package/.github/workflows/commit-lint.yml +0 -85
  14. package/.github/workflows/configs/commitlint.config.cjs +0 -32
  15. package/.github/workflows/configs/commitlint.config.js +0 -32
  16. package/.github/workflows/console-ui.yml +0 -68
  17. package/.github/workflows/create-pr.yml +0 -116
  18. package/.github/workflows/empty-format-test-job.yml +0 -34
  19. package/.github/workflows/format.yml +0 -28
  20. package/.github/workflows/publish.yml +0 -111
  21. package/.github/workflows/secret-scan.yml +0 -51
  22. package/.github/workflows/test.yml +0 -58
  23. package/.github/workflows/umino-project.yml +0 -207
  24. package/.gitleaks.toml +0 -22
  25. package/.prettierignore +0 -31
  26. package/.prettierrc +0 -5
  27. package/CHANGELOG.md +0 -4162
  28. package/CONTRIBUTING.md +0 -60
  29. package/bin/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.js +0 -12
  30. package/bin/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.js.map +0 -1
  31. package/bin/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.js +0 -125
  32. package/bin/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.js.map +0 -1
  33. package/bin/adapter/repositories/issue/issueTimelineUtils.js +0 -38
  34. package/bin/adapter/repositories/issue/issueTimelineUtils.js.map +0 -1
  35. package/bin/domain/entities/UnansweredOwnerCall.js +0 -3
  36. package/bin/domain/entities/UnansweredOwnerCall.js.map +0 -1
  37. package/bin/domain/entities/WorkingTime.js +0 -3
  38. package/bin/domain/entities/WorkingTime.js.map +0 -1
  39. package/bin/domain/usecases/ChangeStatusLongInReviewIssueUseCase.js +0 -37
  40. package/bin/domain/usecases/ChangeStatusLongInReviewIssueUseCase.js.map +0 -1
  41. package/bin/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.js +0 -118
  42. package/bin/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.js.map +0 -1
  43. package/bin/domain/usecases/GenerateWorkingTimeReportUseCase.js +0 -180
  44. package/bin/domain/usecases/GenerateWorkingTimeReportUseCase.js.map +0 -1
  45. package/bin/domain/usecases/TriagerApprovalDispatchUseCase.js +0 -160
  46. package/bin/domain/usecases/TriagerApprovalDispatchUseCase.js.map +0 -1
  47. package/bin/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.js +0 -3
  48. package/bin/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.js.map +0 -1
  49. package/bin/domain/usecases/awaitingOwnerApprovalMessage.js +0 -7
  50. package/bin/domain/usecases/awaitingOwnerApprovalMessage.js.map +0 -1
  51. package/bin/domain/usecases/findLastAgentDeclaringReport.js +0 -12
  52. package/bin/domain/usecases/findLastAgentDeclaringReport.js.map +0 -1
  53. package/bin/domain/usecases/isPullRequestDeclaredUnnecessary.js +0 -25
  54. package/bin/domain/usecases/isPullRequestDeclaredUnnecessary.js.map +0 -1
  55. package/bin/domain/usecases/isWaitingForOwnerApproval.js +0 -16
  56. package/bin/domain/usecases/isWaitingForOwnerApproval.js.map +0 -1
  57. package/bin/domain/usecases/returnedToAwaitingWorkspaceMessage.js +0 -7
  58. package/bin/domain/usecases/returnedToAwaitingWorkspaceMessage.js.map +0 -1
  59. package/commitlint.config.js +0 -6
  60. package/eslint.config.mjs +0 -76
  61. package/jest.config.js +0 -84
  62. package/jest.setup.js +0 -1
  63. package/lib/buffer-equal-constant-time-compat/index.js +0 -17
  64. package/lib/buffer-equal-constant-time-compat/index.test.js +0 -54
  65. package/lib/buffer-equal-constant-time-compat/package.json +0 -5
  66. package/lib/typescript-compat/bin/tsc +0 -2
  67. package/lib/typescript-compat/bin/tsserver +0 -2
  68. package/lib/typescript-compat/lib/typescript.js +0 -1
  69. package/lib/typescript-compat/package.json +0 -13
  70. package/renovate.json +0 -46
  71. package/scripts/copyConsoleUiDist.mjs +0 -35
  72. package/scripts/defaultBranchTipVerify.sh +0 -23
  73. package/scripts/testWorkflowRunVerify.sh +0 -47
  74. package/scripts/verifyConsoleUiDist.mjs +0 -96
  75. package/src/adapter/ci/consoleUiWorkflowTimeout.test.ts +0 -61
  76. package/src/adapter/ci/publishTestWorkflowGate.test.ts +0 -653
  77. package/src/adapter/ci/workflowRunCancellation.test.ts +0 -72
  78. package/src/adapter/entry-points/cli/fleetConfig.test.ts +0 -553
  79. package/src/adapter/entry-points/cli/fleetConfig.ts +0 -266
  80. package/src/adapter/entry-points/cli/index.test.ts +0 -2837
  81. package/src/adapter/entry-points/cli/index.ts +0 -1488
  82. package/src/adapter/entry-points/cli/projectConfig.test.ts +0 -342
  83. package/src/adapter/entry-points/cli/projectConfig.ts +0 -500
  84. package/src/adapter/entry-points/console/consoleAirplaneSnapshotApi.test.ts +0 -658
  85. package/src/adapter/entry-points/console/consoleAirplaneSnapshotApi.ts +0 -482
  86. package/src/adapter/entry-points/console/consoleDataDelivery.test.ts +0 -222
  87. package/src/adapter/entry-points/console/consoleDataDelivery.ts +0 -134
  88. package/src/adapter/entry-points/console/consoleDoneStore.test.ts +0 -169
  89. package/src/adapter/entry-points/console/consoleDoneStore.ts +0 -139
  90. package/src/adapter/entry-points/console/consoleGithubTokenResolver.test.ts +0 -275
  91. package/src/adapter/entry-points/console/consoleGithubTokenResolver.ts +0 -113
  92. package/src/adapter/entry-points/console/consoleImageProxy.test.ts +0 -140
  93. package/src/adapter/entry-points/console/consoleImageProxy.ts +0 -111
  94. package/src/adapter/entry-points/console/consoleItemUrlLookup.test.ts +0 -63
  95. package/src/adapter/entry-points/console/consoleItemUrlLookup.ts +0 -66
  96. package/src/adapter/entry-points/console/consoleOperationApi.test.ts +0 -2523
  97. package/src/adapter/entry-points/console/consoleOperationApi.ts +0 -936
  98. package/src/adapter/entry-points/console/consoleProjectResolver.test.ts +0 -246
  99. package/src/adapter/entry-points/console/consoleProjectResolver.ts +0 -111
  100. package/src/adapter/entry-points/console/consoleReadApi.test.ts +0 -714
  101. package/src/adapter/entry-points/console/consoleReadApi.ts +0 -361
  102. package/src/adapter/entry-points/console/consoleTabNames.test.ts +0 -52
  103. package/src/adapter/entry-points/console/consoleTabNames.ts +0 -37
  104. package/src/adapter/entry-points/console/consoleUiDistSync.test.ts +0 -66
  105. package/src/adapter/entry-points/console/dashboardComposeService.test.ts +0 -595
  106. package/src/adapter/entry-points/console/dashboardComposeService.ts +0 -256
  107. package/src/adapter/entry-points/console/dashboardEmitterComposerKey.test.ts +0 -180
  108. package/src/adapter/entry-points/console/ensureConsoleRunning.test.ts +0 -115
  109. package/src/adapter/entry-points/console/ensureConsoleRunning.ts +0 -64
  110. package/src/adapter/entry-points/console/ui/.storybook/main.ts +0 -12
  111. package/src/adapter/entry-points/console/ui/.storybook/preview.ts +0 -15
  112. package/src/adapter/entry-points/console/ui/biome.json +0 -53
  113. package/src/adapter/entry-points/console/ui/components.json +0 -20
  114. package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +0 -725
  115. package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +0 -831
  116. package/src/adapter/entry-points/console/ui/e2e/playwright.config.ts +0 -30
  117. package/src/adapter/entry-points/console/ui/e2e/tsconfig.json +0 -18
  118. package/src/adapter/entry-points/console/ui/index.html +0 -12
  119. package/src/adapter/entry-points/console/ui/jest-globals.cjs +0 -26
  120. package/src/adapter/entry-points/console/ui/jest.setup.ts +0 -1
  121. package/src/adapter/entry-points/console/ui/jest.styleMock.js +0 -1
  122. package/src/adapter/entry-points/console/ui/public/sw.js +0 -60
  123. package/src/adapter/entry-points/console/ui/src/components/ui/badge.stories.tsx +0 -35
  124. package/src/adapter/entry-points/console/ui/src/components/ui/badge.tsx +0 -28
  125. package/src/adapter/entry-points/console/ui/src/components/ui/button.stories.tsx +0 -34
  126. package/src/adapter/entry-points/console/ui/src/components/ui/button.tsx +0 -50
  127. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleCopyCodeButton.stories.tsx +0 -28
  128. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleCopyCodeButton.test.tsx +0 -65
  129. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleCopyCodeButton.tsx +0 -19
  130. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.stories.tsx +0 -61
  131. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.test.tsx +0 -199
  132. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.tsx +0 -166
  133. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.stories.tsx +0 -22
  134. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.test.tsx +0 -74
  135. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.tsx +0 -84
  136. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagramModalScreen.stories.tsx +0 -27
  137. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagramModalScreen.test.tsx +0 -85
  138. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagramModalScreen.tsx +0 -97
  139. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.stories.tsx +0 -76
  140. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.test.tsx +0 -56
  141. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.tsx +0 -39
  142. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.stories.tsx +0 -32
  143. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.test.tsx +0 -105
  144. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.tsx +0 -157
  145. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.stories.tsx +0 -68
  146. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.test.tsx +0 -612
  147. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.tsx +0 -276
  148. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.stories.tsx +0 -33
  149. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.test.tsx +0 -57
  150. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.tsx +0 -76
  151. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.stories.tsx +0 -25
  152. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.test.tsx +0 -55
  153. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.tsx +0 -49
  154. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCopyUrlButton.stories.tsx +0 -24
  155. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCopyUrlButton.test.tsx +0 -63
  156. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCopyUrlButton.tsx +0 -19
  157. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFetchFailureAlert.stories.tsx +0 -33
  158. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFetchFailureAlert.test.tsx +0 -62
  159. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFetchFailureAlert.tsx +0 -59
  160. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.stories.tsx +0 -38
  161. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.test.tsx +0 -151
  162. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.tsx +0 -245
  163. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.stories.tsx +0 -514
  164. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.test.tsx +0 -727
  165. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.tsx +0 -387
  166. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.stories.tsx +0 -82
  167. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.test.tsx +0 -52
  168. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.tsx +0 -32
  169. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.stories.tsx +0 -48
  170. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.test.tsx +0 -235
  171. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.tsx +0 -145
  172. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestMergeableChip.stories.tsx +0 -29
  173. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestMergeableChip.test.tsx +0 -30
  174. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestMergeableChip.tsx +0 -50
  175. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.stories.tsx +0 -38
  176. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.test.tsx +0 -54
  177. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.tsx +0 -60
  178. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.stories.tsx +0 -56
  179. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.test.tsx +0 -111
  180. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.tsx +0 -92
  181. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.stories.tsx +0 -51
  182. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.test.tsx +0 -59
  183. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.tsx +0 -39
  184. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.stories.tsx +0 -207
  185. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.test.tsx +0 -332
  186. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.tsx +0 -211
  187. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTimerSettingsModalDialog.stories.tsx +0 -49
  188. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTimerSettingsModalDialog.test.tsx +0 -157
  189. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTimerSettingsModalDialog.tsx +0 -118
  190. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleCopyStoryNameButton.stories.tsx +0 -23
  191. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleCopyStoryNameButton.test.tsx +0 -40
  192. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleCopyStoryNameButton.tsx +0 -17
  193. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.stories.tsx +0 -101
  194. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.test.tsx +0 -102
  195. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.tsx +0 -75
  196. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.stories.tsx +0 -40
  197. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.test.tsx +0 -213
  198. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.tsx +0 -115
  199. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedItemRow.stories.tsx +0 -50
  200. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedItemRow.test.tsx +0 -98
  201. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedItemRow.tsx +0 -66
  202. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedList.stories.tsx +0 -69
  203. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedList.test.tsx +0 -101
  204. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedList.tsx +0 -75
  205. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.stories.tsx +0 -137
  206. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.test.tsx +0 -554
  207. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.tsx +0 -400
  208. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryReorderPanel.stories.tsx +0 -30
  209. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryReorderPanel.test.tsx +0 -111
  210. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryReorderPanel.tsx +0 -91
  211. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.stories.tsx +0 -27
  212. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.test.tsx +0 -24
  213. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.tsx +0 -28
  214. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.stories.tsx +0 -14
  215. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.test.tsx +0 -21
  216. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.tsx +0 -26
  217. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.stories.tsx +0 -14
  218. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.test.tsx +0 -40
  219. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.tsx +0 -37
  220. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleErrorToast.stories.tsx +0 -33
  221. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.stories.tsx +0 -20
  222. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.test.tsx +0 -48
  223. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.tsx +0 -42
  224. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOfflinePendingActionsPanel.stories.tsx +0 -115
  225. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOfflinePendingActionsPanel.test.tsx +0 -262
  226. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOfflinePendingActionsPanel.tsx +0 -123
  227. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.stories.tsx +0 -65
  228. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.test.tsx +0 -159
  229. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.tsx +0 -55
  230. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.stories.tsx +0 -20
  231. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.test.tsx +0 -93
  232. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.tsx +0 -45
  233. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.stories.tsx +0 -17
  234. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.test.tsx +0 -49
  235. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.tsx +0 -66
  236. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.stories.tsx +0 -47
  237. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.test.tsx +0 -95
  238. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.tsx +0 -62
  239. package/src/adapter/entry-points/console/ui/src/features/console/components/shared/ConsoleClipboardCopyButton.stories.tsx +0 -32
  240. package/src/adapter/entry-points/console/ui/src/features/console/components/shared/ConsoleClipboardCopyButton.test.tsx +0 -101
  241. package/src/adapter/entry-points/console/ui/src/features/console/components/shared/ConsoleClipboardCopyButton.tsx +0 -80
  242. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useAirplaneMode.test.ts +0 -304
  243. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useAirplaneMode.ts +0 -167
  244. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleActionQueue.test.ts +0 -498
  245. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleActionQueue.ts +0 -277
  246. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.test.ts +0 -99
  247. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.ts +0 -90
  248. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleDetailPrefetch.test.ts +0 -110
  249. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleDetailPrefetch.ts +0 -83
  250. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleFeaturesConfig.test.ts +0 -51
  251. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleFeaturesConfig.ts +0 -39
  252. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.test.ts +0 -229
  253. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.ts +0 -194
  254. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleNavigation.test.ts +0 -151
  255. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleNavigation.ts +0 -128
  256. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.test.ts +0 -629
  257. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts +0 -417
  258. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOverlay.test.ts +0 -40
  259. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOverlay.ts +0 -71
  260. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsolePjcode.test.ts +0 -24
  261. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsolePjcode.ts +0 -17
  262. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectList.test.ts +0 -97
  263. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectList.ts +0 -44
  264. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectTimer.test.ts +0 -59
  265. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectTimer.ts +0 -26
  266. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleReferenceLink.test.ts +0 -48
  267. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleReferenceLink.ts +0 -39
  268. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleResource.test.ts +0 -41
  269. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleResource.ts +0 -57
  270. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleSwipeNavigation.test.ts +0 -115
  271. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleSwipeNavigation.ts +0 -115
  272. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.test.ts +0 -546
  273. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.ts +0 -278
  274. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTimerSettings.test.ts +0 -110
  275. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTimerSettings.ts +0 -70
  276. package/src/adapter/entry-points/console/ui/src/features/console/lib/airplaneSnapshot.test.ts +0 -387
  277. package/src/adapter/entry-points/console/ui/src/features/console/lib/airplaneSnapshot.ts +0 -407
  278. package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.test.ts +0 -446
  279. package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.ts +0 -533
  280. package/src/adapter/entry-points/console/ui/src/features/console/lib/imageProxy.test.ts +0 -78
  281. package/src/adapter/entry-points/console/ui/src/features/console/lib/imageProxy.ts +0 -34
  282. package/src/adapter/entry-points/console/ui/src/features/console/lib/markdown.test.ts +0 -252
  283. package/src/adapter/entry-points/console/ui/src/features/console/lib/markdown.ts +0 -207
  284. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidLoader.test.ts +0 -27
  285. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidLoader.ts +0 -71
  286. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidZoom.test.ts +0 -88
  287. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidZoom.ts +0 -34
  288. package/src/adapter/entry-points/console/ui/src/features/console/lib/navigation.ts +0 -7
  289. package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.test.ts +0 -104
  290. package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.ts +0 -79
  291. package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.test.ts +0 -200
  292. package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.ts +0 -122
  293. package/src/adapter/entry-points/console/ui/src/features/console/logic/clipboard.test.ts +0 -90
  294. package/src/adapter/entry-points/console/ui/src/features/console/logic/clipboard.ts +0 -37
  295. package/src/adapter/entry-points/console/ui/src/features/console/logic/colors.test.ts +0 -34
  296. package/src/adapter/entry-points/console/ui/src/features/console/logic/colors.ts +0 -85
  297. package/src/adapter/entry-points/console/ui/src/features/console/logic/diff.test.ts +0 -81
  298. package/src/adapter/entry-points/console/ui/src/features/console/logic/diff.ts +0 -61
  299. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileStatus.test.ts +0 -35
  300. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileStatus.ts +0 -21
  301. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileTree.test.ts +0 -96
  302. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileTree.ts +0 -93
  303. package/src/adapter/entry-points/console/ui/src/features/console/logic/grouping.test.ts +0 -229
  304. package/src/adapter/entry-points/console/ui/src/features/console/logic/grouping.ts +0 -106
  305. package/src/adapter/entry-points/console/ui/src/features/console/logic/imageFile.test.ts +0 -18
  306. package/src/adapter/entry-points/console/ui/src/features/console/logic/imageFile.ts +0 -16
  307. package/src/adapter/entry-points/console/ui/src/features/console/logic/itemIcons.test.ts +0 -97
  308. package/src/adapter/entry-points/console/ui/src/features/console/logic/itemIcons.ts +0 -95
  309. package/src/adapter/entry-points/console/ui/src/features/console/logic/navigation.test.ts +0 -53
  310. package/src/adapter/entry-points/console/ui/src/features/console/logic/navigation.ts +0 -33
  311. package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.test.ts +0 -72
  312. package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.ts +0 -61
  313. package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.test.ts +0 -325
  314. package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.ts +0 -94
  315. package/src/adapter/entry-points/console/ui/src/features/console/logic/postedComments.test.ts +0 -44
  316. package/src/adapter/entry-points/console/ui/src/features/console/logic/postedComments.ts +0 -17
  317. package/src/adapter/entry-points/console/ui/src/features/console/logic/references.test.ts +0 -103
  318. package/src/adapter/entry-points/console/ui/src/features/console/logic/references.ts +0 -64
  319. package/src/adapter/entry-points/console/ui/src/features/console/logic/relativeTime.test.ts +0 -74
  320. package/src/adapter/entry-points/console/ui/src/features/console/logic/relativeTime.ts +0 -53
  321. package/src/adapter/entry-points/console/ui/src/features/console/logic/swipe.test.ts +0 -34
  322. package/src/adapter/entry-points/console/ui/src/features/console/logic/swipe.ts +0 -28
  323. package/src/adapter/entry-points/console/ui/src/features/console/logic/tabAdvance.test.ts +0 -89
  324. package/src/adapter/entry-points/console/ui/src/features/console/logic/tabAdvance.ts +0 -30
  325. package/src/adapter/entry-points/console/ui/src/features/console/logic/timerSettings.test.ts +0 -61
  326. package/src/adapter/entry-points/console/ui/src/features/console/logic/timerSettings.ts +0 -77
  327. package/src/adapter/entry-points/console/ui/src/features/console/logic/types.ts +0 -173
  328. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +0 -629
  329. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +0 -351
  330. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +0 -1469
  331. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +0 -736
  332. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleReferenceLinkContainer.tsx +0 -25
  333. package/src/adapter/entry-points/console/ui/src/features/console/testing/fixtures.ts +0 -418
  334. package/src/adapter/entry-points/console/ui/src/index.css +0 -1592
  335. package/src/adapter/entry-points/console/ui/src/lib/utils.ts +0 -4
  336. package/src/adapter/entry-points/console/ui/src/main.tsx +0 -21
  337. package/src/adapter/entry-points/console/ui/src/vite-env.d.ts +0 -1
  338. package/src/adapter/entry-points/console/ui/tsconfig.json +0 -26
  339. package/src/adapter/entry-points/console/ui/vite.config.ts +0 -24
  340. package/src/adapter/entry-points/console/ui-dist/assets/index-BYDbK1Fj.js +0 -88
  341. package/src/adapter/entry-points/console/ui-dist/assets/index-KyQISRuQ.css +0 -2
  342. package/src/adapter/entry-points/console/ui-dist/index.html +0 -13
  343. package/src/adapter/entry-points/console/ui-dist/sw.js +0 -60
  344. package/src/adapter/entry-points/console/webServer.test.ts +0 -2945
  345. package/src/adapter/entry-points/console/webServer.ts +0 -940
  346. package/src/adapter/entry-points/function/getStoryObjectMap.ts +0 -3
  347. package/src/adapter/entry-points/handlers/FileSystemConsoleTabsRepository.test.ts +0 -218
  348. package/src/adapter/entry-points/handlers/FileSystemConsoleTabsRepository.ts +0 -113
  349. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.test.ts +0 -110
  350. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.ts +0 -116
  351. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.test.ts +0 -859
  352. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +0 -984
  353. package/src/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.test.ts +0 -126
  354. package/src/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.ts +0 -80
  355. package/src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.test.ts +0 -491
  356. package/src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.ts +0 -137
  357. package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.test.ts +0 -508
  358. package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts +0 -156
  359. package/src/adapter/entry-points/handlers/consoleListsWriter.test.ts +0 -332
  360. package/src/adapter/entry-points/handlers/consoleListsWriter.ts +0 -60
  361. package/src/adapter/entry-points/handlers/dashboardRowWriter.test.ts +0 -112
  362. package/src/adapter/entry-points/handlers/dashboardRowWriter.ts +0 -51
  363. package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.test.ts +0 -285
  364. package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.ts +0 -109
  365. package/src/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.test.ts +0 -170
  366. package/src/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.ts +0 -40
  367. package/src/adapter/entry-points/handlers/machineStatusWriter.test.ts +0 -191
  368. package/src/adapter/entry-points/handlers/machineStatusWriter.ts +0 -110
  369. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +0 -548
  370. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts +0 -192
  371. package/src/adapter/entry-points/handlers/ownerCallFileCleaner.test.ts +0 -111
  372. package/src/adapter/entry-points/handlers/ownerCallFileCleaner.ts +0 -25
  373. package/src/adapter/entry-points/handlers/ownerCallFileStore.test.ts +0 -286
  374. package/src/adapter/entry-points/handlers/ownerCallFileStore.ts +0 -145
  375. package/src/adapter/entry-points/handlers/ownerReplyMarkerDirectoryResolve.test.ts +0 -55
  376. package/src/adapter/entry-points/handlers/ownerReplyMarkerDirectoryResolve.ts +0 -36
  377. package/src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.test.ts +0 -72
  378. package/src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.ts +0 -74
  379. package/src/adapter/entry-points/handlers/rotationOrderFileWriter.test.ts +0 -181
  380. package/src/adapter/entry-points/handlers/rotationOrderFileWriter.ts +0 -18
  381. package/src/adapter/entry-points/handlers/situationFileWriter.test.ts +0 -489
  382. package/src/adapter/entry-points/handlers/situationFileWriter.ts +0 -176
  383. package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.test.ts +0 -169
  384. package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.ts +0 -32
  385. package/src/adapter/entry-points/handlers/subscriptionDisabledTokenHandler.ts +0 -41
  386. package/src/adapter/entry-points/handlers/tokenExhaustionHandover.ts +0 -81
  387. package/src/adapter/entry-points/handlers/tokenStatusWriter.test.ts +0 -266
  388. package/src/adapter/entry-points/handlers/tokenStatusWriter.ts +0 -262
  389. package/src/adapter/proxy/ClaudeMessageResponseParser.test.ts +0 -211
  390. package/src/adapter/proxy/ClaudeMessageResponseParser.ts +0 -181
  391. package/src/adapter/proxy/RateLimitCache.test.ts +0 -1077
  392. package/src/adapter/proxy/RateLimitCache.ts +0 -385
  393. package/src/adapter/proxy/TokenListLoader.test.ts +0 -183
  394. package/src/adapter/proxy/TokenListLoader.ts +0 -75
  395. package/src/adapter/proxy/ensureProxyRunning.test.ts +0 -85
  396. package/src/adapter/proxy/ensureProxyRunning.ts +0 -41
  397. package/src/adapter/proxy/proxyEntry.test.ts +0 -552
  398. package/src/adapter/proxy/proxyEntry.ts +0 -214
  399. package/src/adapter/repositories/BaseGitHubRepository.test.ts +0 -26
  400. package/src/adapter/repositories/BaseGitHubRepository.ts +0 -26
  401. package/src/adapter/repositories/BrowserGitHubProjectRepository.test.ts +0 -108
  402. package/src/adapter/repositories/BrowserGitHubProjectRepository.ts +0 -118
  403. package/src/adapter/repositories/CliGitHubGraphqlRateLimitRepository.test.ts +0 -71
  404. package/src/adapter/repositories/CliGitHubGraphqlRateLimitRepository.ts +0 -21
  405. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.test.ts +0 -324
  406. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts +0 -148
  407. package/src/adapter/repositories/FetchWebhookRepository.ts +0 -8
  408. package/src/adapter/repositories/FileHandoverStateRepository.test.ts +0 -114
  409. package/src/adapter/repositories/FileHandoverStateRepository.ts +0 -73
  410. package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.test.ts +0 -514
  411. package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.ts +0 -109
  412. package/src/adapter/repositories/FileSystemKevReportWatermarkRepository.test.ts +0 -284
  413. package/src/adapter/repositories/FileSystemKevReportWatermarkRepository.ts +0 -128
  414. package/src/adapter/repositories/FileSystemSessionAssistantTurnsRepository.test.ts +0 -138
  415. package/src/adapter/repositories/FileSystemSessionAssistantTurnsRepository.ts +0 -113
  416. package/src/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.test.ts +0 -121
  417. package/src/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.ts +0 -103
  418. package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.test.ts +0 -360
  419. package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.ts +0 -77
  420. package/src/adapter/repositories/FileSystemSessionRecordReader.test.ts +0 -59
  421. package/src/adapter/repositories/FileSystemSessionRecordReader.ts +0 -31
  422. package/src/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.test.ts +0 -287
  423. package/src/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.ts +0 -125
  424. package/src/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.test.ts +0 -285
  425. package/src/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.ts +0 -119
  426. package/src/adapter/repositories/FileSystemSubAgentLivenessResolver.test.ts +0 -237
  427. package/src/adapter/repositories/FileSystemSubAgentLivenessResolver.ts +0 -57
  428. package/src/adapter/repositories/FileSystemSubAgentSilentSecondsResolver.test.ts +0 -56
  429. package/src/adapter/repositories/FileSystemSubAgentSilentSecondsResolver.ts +0 -32
  430. package/src/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.test.ts +0 -68
  431. package/src/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.ts +0 -23
  432. package/src/adapter/repositories/GitHubIssueCheckpointRepository.test.ts +0 -51
  433. package/src/adapter/repositories/GitHubIssueCheckpointRepository.ts +0 -47
  434. package/src/adapter/repositories/GitHubIssueCommentRepository.test.ts +0 -278
  435. package/src/adapter/repositories/GitHubIssueCommentRepository.ts +0 -109
  436. package/src/adapter/repositories/GoogleSpreadsheetRepository.integration.test.ts +0 -265
  437. package/src/adapter/repositories/GoogleSpreadsheetRepository.test.ts +0 -431
  438. package/src/adapter/repositories/GoogleSpreadsheetRepository.ts +0 -268
  439. package/src/adapter/repositories/GraphqlProjectRepository.diskCache.test.ts +0 -272
  440. package/src/adapter/repositories/GraphqlProjectRepository.errorHandling.test.ts +0 -111
  441. package/src/adapter/repositories/GraphqlProjectRepository.fetchProjectId.test.ts +0 -254
  442. package/src/adapter/repositories/GraphqlProjectRepository.test.ts +0 -209
  443. package/src/adapter/repositories/GraphqlProjectRepository.ts +0 -707
  444. package/src/adapter/repositories/GraphqlProjectRepositoryProjectLocation.test.ts +0 -297
  445. package/src/adapter/repositories/KyHttpRepository.ts +0 -8
  446. package/src/adapter/repositories/KySlackRepository.test.ts +0 -172
  447. package/src/adapter/repositories/KySlackRepository.ts +0 -298
  448. package/src/adapter/repositories/LocalCommandIssueAttachmentRepository.test.ts +0 -193
  449. package/src/adapter/repositories/LocalCommandIssueAttachmentRepository.ts +0 -106
  450. package/src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.test.ts +0 -190
  451. package/src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.ts +0 -102
  452. package/src/adapter/repositories/LocalStorageCacheRepository.test.ts +0 -265
  453. package/src/adapter/repositories/LocalStorageCacheRepository.ts +0 -79
  454. package/src/adapter/repositories/LocalStorageRepository.integration.test.ts +0 -142
  455. package/src/adapter/repositories/LocalStorageRepository.test.ts +0 -173
  456. package/src/adapter/repositories/LocalStorageRepository.ts +0 -30
  457. package/src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.test.ts +0 -15
  458. package/src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.ts +0 -9
  459. package/src/adapter/repositories/NodeLocalCommandRunner.largeOutput.test.ts +0 -16
  460. package/src/adapter/repositories/NodeLocalCommandRunner.test.ts +0 -118
  461. package/src/adapter/repositories/NodeLocalCommandRunner.ts +0 -66
  462. package/src/adapter/repositories/NodeProcessSignalRepository.test.ts +0 -82
  463. package/src/adapter/repositories/NodeProcessSignalRepository.ts +0 -47
  464. package/src/adapter/repositories/NodeSubAgentProcessLister.test.ts +0 -44
  465. package/src/adapter/repositories/NodeSubAgentProcessLister.ts +0 -40
  466. package/src/adapter/repositories/NodeTmuxSessionRepository.test.ts +0 -546
  467. package/src/adapter/repositories/NodeTmuxSessionRepository.ts +0 -245
  468. package/src/adapter/repositories/OauthAPIClaudeRepository.ts +0 -279
  469. package/src/adapter/repositories/OauthAPIProxyClaudeRepository.test.ts +0 -166
  470. package/src/adapter/repositories/OauthAPIProxyClaudeRepository.ts +0 -32
  471. package/src/adapter/repositories/OauthProxyClaudeRepository.test.ts +0 -428
  472. package/src/adapter/repositories/OauthProxyClaudeRepository.ts +0 -124
  473. package/src/adapter/repositories/ProcClaudeHandoverSessionRepository.test.ts +0 -242
  474. package/src/adapter/repositories/ProcClaudeHandoverSessionRepository.ts +0 -261
  475. package/src/adapter/repositories/ProcClaudeInteractiveSessionRepository.test.ts +0 -184
  476. package/src/adapter/repositories/ProcClaudeInteractiveSessionRepository.ts +0 -138
  477. package/src/adapter/repositories/ProcClaudeLiveSessionRepository.test.ts +0 -208
  478. package/src/adapter/repositories/ProcClaudeLiveSessionRepository.ts +0 -130
  479. package/src/adapter/repositories/ProcFsProcessEnvironReader.test.ts +0 -60
  480. package/src/adapter/repositories/ProcFsProcessEnvironReader.ts +0 -38
  481. package/src/adapter/repositories/ProcHostMetricsRepository.test.ts +0 -207
  482. package/src/adapter/repositories/ProcHostMetricsRepository.ts +0 -181
  483. package/src/adapter/repositories/ProcTakeOwnershipSpawnRepository.test.ts +0 -231
  484. package/src/adapter/repositories/ProcTakeOwnershipSpawnRepository.ts +0 -134
  485. package/src/adapter/repositories/ProcessListSessionSubAgentActivityRepository.test.ts +0 -121
  486. package/src/adapter/repositories/ProcessListSessionSubAgentActivityRepository.ts +0 -58
  487. package/src/adapter/repositories/ProjectIssuesCacheRepository.test.ts +0 -218
  488. package/src/adapter/repositories/ProjectIssuesCacheRepository.ts +0 -141
  489. package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.test.ts +0 -1195
  490. package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.ts +0 -185
  491. package/src/adapter/repositories/ProxyRateLimitCacheRepository.test.ts +0 -116
  492. package/src/adapter/repositories/ProxyRateLimitCacheRepository.ts +0 -67
  493. package/src/adapter/repositories/RateLimitSnapshotRepository.ts +0 -50
  494. package/src/adapter/repositories/RealSleeper.test.ts +0 -19
  495. package/src/adapter/repositories/RealSleeper.ts +0 -12
  496. package/src/adapter/repositories/RestProjectRepository.test.ts +0 -277
  497. package/src/adapter/repositories/RestProjectRepository.ts +0 -129
  498. package/src/adapter/repositories/SqliteClaudeMessageResponseRepository.test.ts +0 -313
  499. package/src/adapter/repositories/SqliteClaudeMessageResponseRepository.ts +0 -164
  500. package/src/adapter/repositories/SystemDateRepository.test.ts +0 -60
  501. package/src/adapter/repositories/SystemDateRepository.ts +0 -34
  502. package/src/adapter/repositories/TmuxSilentSessionNotificationRepository.test.ts +0 -253
  503. package/src/adapter/repositories/TmuxSilentSessionNotificationRepository.ts +0 -158
  504. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.queuedReply.test.ts +0 -171
  505. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.singleScan.test.ts +0 -86
  506. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.test.ts +0 -996
  507. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts +0 -396
  508. package/src/adapter/repositories/TranscriptRefusalTailStatusProvider.test.ts +0 -208
  509. package/src/adapter/repositories/TranscriptRefusalTailStatusProvider.ts +0 -80
  510. package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.test.ts +0 -1674
  511. package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.ts +0 -459
  512. package/src/adapter/repositories/clSessionScopeUnitName.test.ts +0 -76
  513. package/src/adapter/repositories/clSessionScopeUnitName.ts +0 -5
  514. package/src/adapter/repositories/clSessionScopeUnitNameFromCgroupContent.test.ts +0 -34
  515. package/src/adapter/repositories/clSessionScopeUnitNameFromCgroupContent.ts +0 -8
  516. package/src/adapter/repositories/githubGraphqlClient.test.ts +0 -479
  517. package/src/adapter/repositories/githubGraphqlClient.ts +0 -243
  518. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +0 -5182
  519. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +0 -3303
  520. package/src/adapter/repositories/issue/ApiV3IssueRepository.test.ts +0 -46
  521. package/src/adapter/repositories/issue/ApiV3IssueRepository.ts +0 -88
  522. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +0 -1776
  523. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.ts +0 -1552
  524. package/src/adapter/repositories/issue/RestIssueRepository.test.ts +0 -393
  525. package/src/adapter/repositories/issue/RestIssueRepository.ts +0 -252
  526. package/src/adapter/repositories/issue/gitHubRawUrl.test.ts +0 -29
  527. package/src/adapter/repositories/issue/gitHubRawUrl.ts +0 -30
  528. package/src/adapter/repositories/issue/githubRateLimitRetry.test.ts +0 -167
  529. package/src/adapter/repositories/issue/githubRateLimitRetry.ts +0 -109
  530. package/src/adapter/repositories/localStorageCacheDirectory.test.ts +0 -81
  531. package/src/adapter/repositories/localStorageCacheDirectory.ts +0 -13
  532. package/src/adapter/repositories/projectFieldDefinition.test.ts +0 -191
  533. package/src/adapter/repositories/projectFieldDefinition.ts +0 -144
  534. package/src/adapter/repositories/utils.test.ts +0 -55
  535. package/src/adapter/repositories/utils.ts +0 -45
  536. package/src/domain/entities/ClaudeHandoverSession.ts +0 -12
  537. package/src/domain/entities/ClaudeMessageResponse.ts +0 -31
  538. package/src/domain/entities/ClaudeTokenUsage.ts +0 -17
  539. package/src/domain/entities/ClaudeWindowUsage.ts +0 -5
  540. package/src/domain/entities/Comment.ts +0 -5
  541. package/src/domain/entities/InteractiveLiveSession.ts +0 -6
  542. package/src/domain/entities/Issue.ts +0 -31
  543. package/src/domain/entities/KevReportWatermark.ts +0 -4
  544. package/src/domain/entities/LiveSessionActivitySnapshot.ts +0 -14
  545. package/src/domain/entities/LiveSessionOutputActivity.ts +0 -4
  546. package/src/domain/entities/LiveSessionProcessSnapshot.ts +0 -18
  547. package/src/domain/entities/LiveTmuxSession.ts +0 -4
  548. package/src/domain/entities/Member.ts +0 -3
  549. package/src/domain/entities/OutputDegeneration.ts +0 -10
  550. package/src/domain/entities/Project.ts +0 -88
  551. package/src/domain/entities/ProjectField.ts +0 -3
  552. package/src/domain/entities/ProjectFieldName.ts +0 -2
  553. package/src/domain/entities/ProjectFieldSingleSelect.ts +0 -8
  554. package/src/domain/entities/ProjectFieldSingleSelectOption.ts +0 -8
  555. package/src/domain/entities/RequiredProjectField.ts +0 -68
  556. package/src/domain/entities/SearchedIssue.ts +0 -11
  557. package/src/domain/entities/StoryObjectMap.ts +0 -12
  558. package/src/domain/entities/TokenExhaustionHandoverState.ts +0 -8
  559. package/src/domain/entities/WorkflowStatus.ts +0 -66
  560. package/src/domain/usecases/ActionAnnouncementUseCase.test.ts +0 -91
  561. package/src/domain/usecases/ActionAnnouncementUseCase.ts +0 -78
  562. package/src/domain/usecases/AgentDesignationLabelAdoptUseCase.test.ts +0 -222
  563. package/src/domain/usecases/AgentDesignationLabelAdoptUseCase.ts +0 -76
  564. package/src/domain/usecases/AnalyzeProblemByIssueUseCase.test.ts +0 -118
  565. package/src/domain/usecases/AnalyzeProblemByIssueUseCase.ts +0 -112
  566. package/src/domain/usecases/AnalyzeStoriesUseCase.test.ts +0 -82
  567. package/src/domain/usecases/AnalyzeStoriesUseCase.ts +0 -296
  568. package/src/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.test.ts +0 -578
  569. package/src/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.ts +0 -137
  570. package/src/domain/usecases/CONTRIBUTING_FOR_TEST.md +0 -259
  571. package/src/domain/usecases/ChangeStatusByStoryColorUseCase.test.ts +0 -437
  572. package/src/domain/usecases/ChangeStatusByStoryColorUseCase.ts +0 -80
  573. package/src/domain/usecases/ChangeTargetPullRequestApprover.test.ts +0 -248
  574. package/src/domain/usecases/ChangeTargetPullRequestApprover.ts +0 -69
  575. package/src/domain/usecases/CheckIssueReviewReadinessUseCase.test.ts +0 -411
  576. package/src/domain/usecases/CheckIssueReviewReadinessUseCase.ts +0 -142
  577. package/src/domain/usecases/ClearDependedIssueURLUseCase.test.ts +0 -1120
  578. package/src/domain/usecases/ClearDependedIssueURLUseCase.ts +0 -161
  579. package/src/domain/usecases/ClearPastNextActionDateHourUseCase.test.ts +0 -345
  580. package/src/domain/usecases/ClearPastNextActionDateHourUseCase.ts +0 -81
  581. package/src/domain/usecases/CliErrorReportUseCase.test.ts +0 -162
  582. package/src/domain/usecases/CliErrorReportUseCase.ts +0 -72
  583. package/src/domain/usecases/ConflictedIssueRevertUseCase.test.ts +0 -886
  584. package/src/domain/usecases/ConflictedIssueRevertUseCase.ts +0 -146
  585. package/src/domain/usecases/CreateEstimationIssueUseCase.test.ts +0 -227
  586. package/src/domain/usecases/CreateEstimationIssueUseCase.ts +0 -205
  587. package/src/domain/usecases/CreateNewStoryByLabelUseCase.test.ts +0 -1084
  588. package/src/domain/usecases/CreateNewStoryByLabelUseCase.ts +0 -146
  589. package/src/domain/usecases/DailySecurityScanUseCase.test.ts +0 -1709
  590. package/src/domain/usecases/DailySecurityScanUseCase.ts +0 -476
  591. package/src/domain/usecases/DefaultSilentSessionMessageComposer.test.ts +0 -533
  592. package/src/domain/usecases/DefaultSilentSessionMessageComposer.ts +0 -122
  593. package/src/domain/usecases/GetStoryObjectMapUseCase.test.ts +0 -330
  594. package/src/domain/usecases/GetStoryObjectMapUseCase.ts +0 -79
  595. package/src/domain/usecases/HandleScheduledEventUseCase.test.ts +0 -1913
  596. package/src/domain/usecases/HandleScheduledEventUseCase.ts +0 -846
  597. package/src/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.test.ts +0 -175
  598. package/src/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.ts +0 -63
  599. package/src/domain/usecases/IssueNoStatusUpdateUseCase.test.ts +0 -165
  600. package/src/domain/usecases/IssueNoStatusUpdateUseCase.ts +0 -44
  601. package/src/domain/usecases/IssueRejectionEvaluator.test.ts +0 -1137
  602. package/src/domain/usecases/IssueRejectionEvaluator.ts +0 -340
  603. package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.test.ts +0 -679
  604. package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts +0 -178
  605. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +0 -5441
  606. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +0 -891
  607. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +0 -2069
  608. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +0 -489
  609. package/src/domain/usecases/OauthTokenSelectUseCase.test.ts +0 -662
  610. package/src/domain/usecases/OauthTokenSelectUseCase.ts +0 -274
  611. package/src/domain/usecases/OutputDegenerationDetector.test.ts +0 -167
  612. package/src/domain/usecases/OutputDegenerationDetector.ts +0 -169
  613. package/src/domain/usecases/ProjectRequiredFieldCreateUseCase.test.ts +0 -711
  614. package/src/domain/usecases/ProjectRequiredFieldCreateUseCase.ts +0 -146
  615. package/src/domain/usecases/QualityCheckAdvanceUseCase.test.ts +0 -421
  616. package/src/domain/usecases/QualityCheckAdvanceUseCase.ts +0 -76
  617. package/src/domain/usecases/ReopenedDoneIssueRevertUseCase.test.ts +0 -188
  618. package/src/domain/usecases/ReopenedDoneIssueRevertUseCase.ts +0 -57
  619. package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.test.ts +0 -303
  620. package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.ts +0 -155
  621. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.test.ts +0 -1838
  622. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.ts +0 -221
  623. package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +0 -2392
  624. package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +0 -499
  625. package/src/domain/usecases/SessionOutputDegenerationRecoveryUseCase.test.ts +0 -224
  626. package/src/domain/usecases/SessionOutputDegenerationRecoveryUseCase.ts +0 -142
  627. package/src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.test.ts +0 -360
  628. package/src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.ts +0 -70
  629. package/src/domain/usecases/SetNoStoryIssueToStoryUseCase.test.ts +0 -400
  630. package/src/domain/usecases/SetNoStoryIssueToStoryUseCase.ts +0 -48
  631. package/src/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.test.ts +0 -696
  632. package/src/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.ts +0 -201
  633. package/src/domain/usecases/SetupTowerDefenceProjectUseCase.test.ts +0 -1363
  634. package/src/domain/usecases/SetupTowerDefenceProjectUseCase.ts +0 -215
  635. package/src/domain/usecases/StaleTaskPullRequestCloseUseCase.test.ts +0 -170
  636. package/src/domain/usecases/StaleTaskPullRequestCloseUseCase.ts +0 -38
  637. package/src/domain/usecases/StaleTmuxSessionKillUseCase.test.ts +0 -283
  638. package/src/domain/usecases/StaleTmuxSessionKillUseCase.ts +0 -98
  639. package/src/domain/usecases/StartPreparationUseCase.test.ts +0 -7822
  640. package/src/domain/usecases/StartPreparationUseCase.ts +0 -876
  641. package/src/domain/usecases/SubscriptionDisabledIssueUseCase.test.ts +0 -200
  642. package/src/domain/usecases/SubscriptionDisabledIssueUseCase.ts +0 -74
  643. package/src/domain/usecases/TokenExhaustionHandoverUseCase.test.ts +0 -838
  644. package/src/domain/usecases/TokenExhaustionHandoverUseCase.ts +0 -439
  645. package/src/domain/usecases/UpdateIssueStatusByLabelUseCase.test.ts +0 -306
  646. package/src/domain/usecases/UpdateIssueStatusByLabelUseCase.ts +0 -64
  647. package/src/domain/usecases/UpdateRateLimitCacheUseCase.test.ts +0 -178
  648. package/src/domain/usecases/UpdateRateLimitCacheUseCase.ts +0 -22
  649. package/src/domain/usecases/adapter-interfaces/ClaudeHandoverSessionRepository.ts +0 -5
  650. package/src/domain/usecases/adapter-interfaces/ClaudeInteractiveSessionRepository.ts +0 -9
  651. package/src/domain/usecases/adapter-interfaces/ClaudeLiveSessionRepository.ts +0 -8
  652. package/src/domain/usecases/adapter-interfaces/ClaudeMessageResponseRepository.ts +0 -5
  653. package/src/domain/usecases/adapter-interfaces/ClaudeRepository.ts +0 -6
  654. package/src/domain/usecases/adapter-interfaces/ClaudeTokenUsageRepository.ts +0 -8
  655. package/src/domain/usecases/adapter-interfaces/ConsoleTabsRepository.ts +0 -12
  656. package/src/domain/usecases/adapter-interfaces/CopilotRepository.ts +0 -3
  657. package/src/domain/usecases/adapter-interfaces/DateRepository.ts +0 -7
  658. package/src/domain/usecases/adapter-interfaces/GitHubGraphqlRateLimitRepository.ts +0 -3
  659. package/src/domain/usecases/adapter-interfaces/HttpRepository.ts +0 -3
  660. package/src/domain/usecases/adapter-interfaces/InteractiveLiveSessionTranscriptResolver.ts +0 -7
  661. package/src/domain/usecases/adapter-interfaces/IssueAttachmentRepository.ts +0 -9
  662. package/src/domain/usecases/adapter-interfaces/IssueCheckpointRepository.ts +0 -3
  663. package/src/domain/usecases/adapter-interfaces/IssueCommentRepository.ts +0 -7
  664. package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +0 -229
  665. package/src/domain/usecases/adapter-interfaces/KevReportWatermarkRepository.ts +0 -11
  666. package/src/domain/usecases/adapter-interfaces/LiveSessionProcessSnapshotProvider.ts +0 -5
  667. package/src/domain/usecases/adapter-interfaces/LocalCommandRunner.ts +0 -16
  668. package/src/domain/usecases/adapter-interfaces/OwnerCallStatusProvider.ts +0 -5
  669. package/src/domain/usecases/adapter-interfaces/ProcessEnvironReader.ts +0 -3
  670. package/src/domain/usecases/adapter-interfaces/ProcessSignalRepository.ts +0 -5
  671. package/src/domain/usecases/adapter-interfaces/ProjectRepository.ts +0 -29
  672. package/src/domain/usecases/adapter-interfaces/RateLimitCacheRepository.ts +0 -10
  673. package/src/domain/usecases/adapter-interfaces/RefusalTailStatusProvider.ts +0 -5
  674. package/src/domain/usecases/adapter-interfaces/SessionAssistantTurnsRepository.ts +0 -6
  675. package/src/domain/usecases/adapter-interfaces/SessionDegenerationCooldownStateRepository.ts +0 -4
  676. package/src/domain/usecases/adapter-interfaces/SessionOutputActivityRepository.ts +0 -7
  677. package/src/domain/usecases/adapter-interfaces/SessionRecordReader.ts +0 -3
  678. package/src/domain/usecases/adapter-interfaces/SessionSubAgentActivityRepository.ts +0 -8
  679. package/src/domain/usecases/adapter-interfaces/SilentSessionCandidateStateRepository.ts +0 -10
  680. package/src/domain/usecases/adapter-interfaces/SilentSessionHubTaskStatusCacheRepository.ts +0 -20
  681. package/src/domain/usecases/adapter-interfaces/SilentSessionMessageComposer.ts +0 -18
  682. package/src/domain/usecases/adapter-interfaces/SilentSessionNotificationRepository.ts +0 -6
  683. package/src/domain/usecases/adapter-interfaces/SlackRepository.ts +0 -20
  684. package/src/domain/usecases/adapter-interfaces/Sleeper.ts +0 -3
  685. package/src/domain/usecases/adapter-interfaces/SpreadsheetRepository.ts +0 -18
  686. package/src/domain/usecases/adapter-interfaces/StatusDefaultRepository.ts +0 -5
  687. package/src/domain/usecases/adapter-interfaces/SubAgentLivenessResolver.ts +0 -6
  688. package/src/domain/usecases/adapter-interfaces/SubAgentProcessLister.ts +0 -8
  689. package/src/domain/usecases/adapter-interfaces/SubAgentSilentSecondsResolver.ts +0 -3
  690. package/src/domain/usecases/adapter-interfaces/SubAgentTranscriptDirectoryResolver.ts +0 -6
  691. package/src/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.ts +0 -9
  692. package/src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts +0 -20
  693. package/src/domain/usecases/adapter-interfaces/TokenExhaustionHandoverStateRepository.ts +0 -6
  694. package/src/domain/usecases/adapter-interfaces/TokenRateLimitSnapshotRepository.ts +0 -22
  695. package/src/domain/usecases/adapter-interfaces/WebhookRepository.ts +0 -3
  696. package/src/domain/usecases/agentReportPrefix.ts +0 -1
  697. package/src/domain/usecases/autoStatusCheckComments.ts +0 -1
  698. package/src/domain/usecases/console/.gitkeep +0 -0
  699. package/src/domain/usecases/console/GenerateConsoleListsUseCase.test.ts +0 -887
  700. package/src/domain/usecases/console/GenerateConsoleListsUseCase.ts +0 -368
  701. package/src/domain/usecases/dashboard/ComposeDashboardUseCase.test.ts +0 -654
  702. package/src/domain/usecases/dashboard/ComposeDashboardUseCase.ts +0 -318
  703. package/src/domain/usecases/dashboard/DashboardProjectCode.test.ts +0 -34
  704. package/src/domain/usecases/dashboard/DashboardProjectCode.ts +0 -27
  705. package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.test.ts +0 -160
  706. package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.ts +0 -69
  707. package/src/domain/usecases/dashboard/GenerateTokenStatusUseCase.test.ts +0 -362
  708. package/src/domain/usecases/dashboard/GenerateTokenStatusUseCase.ts +0 -234
  709. package/src/domain/usecases/ensureAgentOptionAndGetId.ts +0 -47
  710. package/src/domain/usecases/extractFencedJsonBlocks.test.ts +0 -40
  711. package/src/domain/usecases/extractFencedJsonBlocks.ts +0 -21
  712. package/src/domain/usecases/extractNextStepAgent.test.ts +0 -58
  713. package/src/domain/usecases/extractNextStepAgent.ts +0 -18
  714. package/src/domain/usecases/extractNextStepAgentFromComments.ts +0 -13
  715. package/src/domain/usecases/findLastAgentReport.test.ts +0 -121
  716. package/src/domain/usecases/findLastAgentReport.ts +0 -14
  717. package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.test.ts +0 -339
  718. package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.ts +0 -188
  719. package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.test.ts +0 -389
  720. package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.ts +0 -103
  721. package/src/domain/usecases/intmux/OwnerCallFile.test.ts +0 -184
  722. package/src/domain/usecases/intmux/OwnerCallFile.ts +0 -80
  723. package/src/domain/usecases/isAgentReportBody.test.ts +0 -108
  724. package/src/domain/usecases/isAgentReportBody.ts +0 -52
  725. package/src/domain/usecases/isAuthorAuthorizedForAutoStatusCheck.ts +0 -12
  726. package/src/domain/usecases/isHumanComment.test.ts +0 -77
  727. package/src/domain/usecases/isHumanComment.ts +0 -23
  728. package/src/domain/usecases/isRecord.ts +0 -2
  729. package/src/domain/usecases/issueReactivationTriggerIsPending.test.ts +0 -541
  730. package/src/domain/usecases/issueReactivationTriggerIsPending.ts +0 -25
  731. package/src/domain/usecases/nextStepAgentDispatchRepeatedMessage.ts +0 -2
  732. package/src/domain/usecases/normalizeReportBody.test.ts +0 -24
  733. package/src/domain/usecases/normalizeReportBody.ts +0 -2
  734. package/src/domain/usecases/resolveAllowedIssueAuthors.test.ts +0 -51
  735. package/src/domain/usecases/resolveAllowedIssueAuthors.ts +0 -6
  736. package/src/domain/usecases/resolveLabelsAsLlmAgentName.test.ts +0 -42
  737. package/src/domain/usecases/resolveLabelsAsLlmAgentName.ts +0 -6
  738. package/src/domain/usecases/resolveLabelsNotRequiringPullRequest.test.ts +0 -33
  739. package/src/domain/usecases/resolveLabelsNotRequiringPullRequest.ts +0 -7
  740. package/src/domain/usecases/resolveNextStepAgentDispatchRepetition.test.ts +0 -337
  741. package/src/domain/usecases/resolveNextStepAgentDispatchRepetition.ts +0 -133
  742. package/src/domain/usecases/silentSessionReminderSentinel.ts +0 -9
  743. package/src/domain/usecases/triagerAgentName.ts +0 -4
  744. package/src/domain/usecases/utils.test.ts +0 -35
  745. package/src/domain/usecases/utils.ts +0 -6
  746. package/src/index.test.ts +0 -8
  747. package/src/index.ts +0 -10
  748. package/src/releaseConfig.test.ts +0 -40
  749. package/tsconfig.build.json +0 -18
  750. package/tsconfig.json +0 -25
  751. package/types/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.d.ts +0 -13
  752. package/types/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.d.ts.map +0 -1
  753. package/types/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.d.ts +0 -16
  754. package/types/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.d.ts.map +0 -1
  755. package/types/adapter/repositories/issue/issueTimelineUtils.d.ts +0 -12
  756. package/types/adapter/repositories/issue/issueTimelineUtils.d.ts.map +0 -1
  757. package/types/domain/entities/UnansweredOwnerCall.d.ts +0 -5
  758. package/types/domain/entities/UnansweredOwnerCall.d.ts.map +0 -1
  759. package/types/domain/entities/WorkingTime.d.ts +0 -8
  760. package/types/domain/entities/WorkingTime.d.ts.map +0 -1
  761. package/types/domain/usecases/ChangeStatusLongInReviewIssueUseCase.d.ts +0 -17
  762. package/types/domain/usecases/ChangeStatusLongInReviewIssueUseCase.d.ts.map +0 -1
  763. package/types/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.d.ts +0 -23
  764. package/types/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.d.ts.map +0 -1
  765. package/types/domain/usecases/GenerateWorkingTimeReportUseCase.d.ts +0 -50
  766. package/types/domain/usecases/GenerateWorkingTimeReportUseCase.d.ts.map +0 -1
  767. package/types/domain/usecases/TriagerApprovalDispatchUseCase.d.ts +0 -15
  768. package/types/domain/usecases/TriagerApprovalDispatchUseCase.d.ts.map +0 -1
  769. package/types/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.d.ts +0 -11
  770. package/types/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.d.ts.map +0 -1
  771. package/types/domain/usecases/awaitingOwnerApprovalMessage.d.ts +0 -3
  772. package/types/domain/usecases/awaitingOwnerApprovalMessage.d.ts.map +0 -1
  773. package/types/domain/usecases/findLastAgentDeclaringReport.d.ts +0 -5
  774. package/types/domain/usecases/findLastAgentDeclaringReport.d.ts.map +0 -1
  775. package/types/domain/usecases/isPullRequestDeclaredUnnecessary.d.ts +0 -5
  776. package/types/domain/usecases/isPullRequestDeclaredUnnecessary.d.ts.map +0 -1
  777. package/types/domain/usecases/isWaitingForOwnerApproval.d.ts +0 -2
  778. package/types/domain/usecases/isWaitingForOwnerApproval.d.ts.map +0 -1
  779. package/types/domain/usecases/returnedToAwaitingWorkspaceMessage.d.ts +0 -3
  780. package/types/domain/usecases/returnedToAwaitingWorkspaceMessage.d.ts.map +0 -1
@@ -1,3303 +0,0 @@
1
- import {
2
- IssueRepository,
3
- RelatedPullRequest,
4
- OpenPullRequestCiStatus,
5
- IssueComment,
6
- PullRequestDetail,
7
- PullRequestFile,
8
- PullRequestCommit,
9
- PullRequestReviewCommentSide,
10
- PullRequestReviewInlineLocation,
11
- } from '../../../domain/usecases/adapter-interfaces/IssueRepository';
12
- import { FieldOption, Project } from '../../../domain/entities/Project';
13
- import { Issue } from '../../../domain/entities/Issue';
14
- import { SearchedIssue } from '../../../domain/entities/SearchedIssue';
15
- import { StoryObjectMap } from '../../../domain/entities/StoryObjectMap';
16
- import { ApiV3IssueRepository } from './ApiV3IssueRepository';
17
- import { normalizeGitHubRawUrl } from './gitHubRawUrl';
18
- import { RestIssueRepository } from './RestIssueRepository';
19
- import {
20
- GraphqlProjectItemRepository,
21
- ProjectItem,
22
- } from './GraphqlProjectItemRepository';
23
- import { LocalStorageCacheRepository } from '../LocalStorageCacheRepository';
24
- import {
25
- CachedProjectIssues,
26
- isIssueArray,
27
- isProject,
28
- ProjectIssuesCacheRepository,
29
- } from '../ProjectIssuesCacheRepository';
30
- import { BaseGitHubRepository } from '../BaseGitHubRepository';
31
- import {
32
- fetchGithubGraphql,
33
- postGithubGraphqlJson,
34
- } from '../githubGraphqlClient';
35
- import { normalizeFieldName } from '../utils';
36
- import { LocalStorageRepository } from '../LocalStorageRepository';
37
- import { Member } from '../../../domain/entities/Member';
38
- import { ProjectRepository } from '../../../domain/usecases/adapter-interfaces/ProjectRepository';
39
- import { DateRepository } from '../../../domain/usecases/adapter-interfaces/DateRepository';
40
- import {
41
- Sleep,
42
- realSleep,
43
- fetchWithGitHubRateLimitRetry,
44
- computeRateLimitResetIso,
45
- hasRateLimitSignals,
46
- GitHubRateLimitError,
47
- } from './githubRateLimitRetry';
48
-
49
- export const FULL_ISSUE_FETCH_INTERVAL_MS = 60 * 60 * 1000;
50
- export const INCREMENTAL_FETCH_SKEW_BUFFER_MS = 5 * 60 * 1000;
51
- export const REQUIRED_CHECKS_CACHE_TTL_MS = 10 * 60 * 1000;
52
-
53
- const SELF_AUTHORED_REVIEW_REFUSAL =
54
- 'Can not request changes on your own pull request';
55
-
56
- type TimelineItem = {
57
- __typename: string;
58
- willCloseTarget?: boolean;
59
- source?: {
60
- __typename: string;
61
- url?: string;
62
- number?: number;
63
- body?: string | null;
64
- state?: string;
65
- createdAt?: string;
66
- isDraft?: boolean;
67
- mergeable?: string;
68
- headRefName?: string;
69
- baseRefName?: string;
70
- baseRef?: {
71
- name: string;
72
- } | null;
73
- };
74
- };
75
-
76
- type IssueTimelineResponse = {
77
- data?: {
78
- repository?: {
79
- issue?: {
80
- timelineItems: {
81
- pageInfo: {
82
- endCursor: string;
83
- hasNextPage: boolean;
84
- };
85
- nodes: TimelineItem[];
86
- };
87
- };
88
- };
89
- };
90
- errors?: Array<{ message: string }>;
91
- };
92
-
93
- type CiContextNode =
94
- | {
95
- __typename: 'CheckRun';
96
- name: string;
97
- conclusion: string | null;
98
- databaseId: number;
99
- }
100
- | {
101
- __typename: 'StatusContext';
102
- context: string;
103
- state: string;
104
- };
105
-
106
- type PrStatusComputationData = {
107
- isDraft?: boolean;
108
- mergeable?: string;
109
- requiredCheckNames: string[];
110
- ciContexts: CiContextNode[];
111
- reviewThreads: Array<{
112
- isResolved: boolean;
113
- }>;
114
- };
115
-
116
- // One GraphQL query carrying this many aliased pull requests costs the same
117
- // single rate-limit point as a query carrying one, measured against the live
118
- // API by reading the rateLimit cost field it returns.
119
- const SLIM_PULL_REQUEST_BATCH_SIZE = 100;
120
- const SLIM_PULL_REQUEST_REVIEW_THREADS_PAGE_SIZE = 100;
121
-
122
- type SlimPullRequestNodeResponse = {
123
- url: string;
124
- state: string;
125
- isDraft?: boolean;
126
- headRefName?: string;
127
- baseRefName?: string;
128
- mergeable?: string;
129
- headRefOid?: string;
130
- reviewThreads?: {
131
- pageInfo: {
132
- endCursor: string | null;
133
- hasNextPage: boolean;
134
- };
135
- nodes: Array<{
136
- isResolved: boolean;
137
- }>;
138
- };
139
- };
140
-
141
- type SlimPullRequestBatchResponse = {
142
- data?: Record<
143
- string,
144
- { pullRequest?: SlimPullRequestNodeResponse | null } | null
145
- > | null;
146
- errors?: Array<{
147
- message: string;
148
- type?: string;
149
- path?: Array<string | number>;
150
- }>;
151
- };
152
-
153
- type SlimPullRequestResponse = {
154
- data?: {
155
- repository?: {
156
- pullRequest?: {
157
- url: string;
158
- state: string;
159
- isDraft?: boolean;
160
- headRefName?: string;
161
- baseRefName?: string;
162
- mergeable?: string;
163
- headRefOid?: string;
164
- reviewThreads?: {
165
- pageInfo: {
166
- endCursor: string | null;
167
- hasNextPage: boolean;
168
- };
169
- nodes: Array<{
170
- isResolved: boolean;
171
- }>;
172
- };
173
- } | null;
174
- } | null;
175
- };
176
- errors?: Array<{ message: string }>;
177
- };
178
-
179
- type SlimPullRequest = {
180
- url: string;
181
- state: string;
182
- isDraft?: boolean;
183
- headRefName?: string;
184
- baseRefName?: string;
185
- mergeable?: string;
186
- headRefOid?: string;
187
- reviewThreads: Array<{
188
- isResolved: boolean;
189
- }>;
190
- };
191
-
192
- type BranchRulesResponseItem = {
193
- type: string;
194
- parameters?: {
195
- required_status_checks?: Array<{
196
- context: string;
197
- }>;
198
- };
199
- };
200
-
201
- type BranchDetailResponse = {
202
- protection?: {
203
- required_status_checks?: {
204
- contexts?: string[];
205
- } | null;
206
- } | null;
207
- };
208
-
209
- type CheckRunsResponse = {
210
- total_count: number;
211
- check_runs: Array<{
212
- id: number;
213
- name: string;
214
- conclusion: string | null;
215
- }>;
216
- };
217
-
218
- type CheckSuitesResponse = {
219
- total_count: number;
220
- check_suites: Array<{
221
- id: number;
222
- }>;
223
- };
224
-
225
- type CombinedStatusResponse = {
226
- statuses: Array<{
227
- context: string;
228
- state: string;
229
- }>;
230
- };
231
-
232
- type PullRequestMergeabilityResponse = {
233
- data?: {
234
- repository?: {
235
- pullRequest?: {
236
- mergeable?: string | null;
237
- mergeStateStatus?: string | null;
238
- } | null;
239
- } | null;
240
- };
241
- errors?: Array<{ message: string }>;
242
- };
243
-
244
- function isIssueTimelineResponse(
245
- value: unknown,
246
- ): value is IssueTimelineResponse {
247
- if (typeof value !== 'object' || value === null) return false;
248
- return true;
249
- }
250
-
251
- function isSlimPullRequestResponse(
252
- value: unknown,
253
- ): value is SlimPullRequestResponse {
254
- if (typeof value !== 'object' || value === null) return false;
255
- return true;
256
- }
257
-
258
- function isSlimPullRequestBatchResponse(
259
- value: unknown,
260
- ): value is SlimPullRequestBatchResponse {
261
- if (typeof value !== 'object' || value === null) return false;
262
- return true;
263
- }
264
-
265
- function isBranchRulesResponse(
266
- value: unknown,
267
- ): value is BranchRulesResponseItem[] {
268
- return Array.isArray(value);
269
- }
270
-
271
- function isBranchDetailResponse(value: unknown): value is BranchDetailResponse {
272
- if (typeof value !== 'object' || value === null) return false;
273
- return true;
274
- }
275
-
276
- function isCheckRunsResponse(value: unknown): value is CheckRunsResponse {
277
- if (typeof value !== 'object' || value === null) return false;
278
- return 'check_runs' in value && Array.isArray(value.check_runs);
279
- }
280
-
281
- function isCheckSuitesResponse(value: unknown): value is CheckSuitesResponse {
282
- if (typeof value !== 'object' || value === null) return false;
283
- return 'check_suites' in value && Array.isArray(value.check_suites);
284
- }
285
-
286
- function isCombinedStatusResponse(
287
- value: unknown,
288
- ): value is CombinedStatusResponse {
289
- if (typeof value !== 'object' || value === null) return false;
290
- return 'statuses' in value && Array.isArray(value.statuses);
291
- }
292
-
293
- type AuthenticatedUserResponse = {
294
- login: string;
295
- };
296
-
297
- function isAuthenticatedUserResponse(
298
- value: unknown,
299
- ): value is AuthenticatedUserResponse {
300
- if (typeof value !== 'object' || value === null) return false;
301
- if (!('login' in value)) return false;
302
- return typeof value.login === 'string';
303
- }
304
-
305
- function isPullRequestMergeabilityResponse(
306
- value: unknown,
307
- ): value is PullRequestMergeabilityResponse {
308
- if (typeof value !== 'object' || value === null) return false;
309
- return true;
310
- }
311
-
312
- type PullRequestFilesResponseItem = {
313
- filename: string;
314
- };
315
-
316
- function isPullRequestFilesResponse(
317
- value: unknown,
318
- ): value is PullRequestFilesResponseItem[] {
319
- if (!Array.isArray(value)) return false;
320
- return value.every(
321
- (item) => typeof item === 'object' && item !== null && 'filename' in item,
322
- );
323
- }
324
-
325
- function isRecord(value: unknown): value is Record<string, unknown> {
326
- return typeof value === 'object' && value !== null;
327
- }
328
-
329
- type RestPullRequestCiStatusResponse = {
330
- html_url: string;
331
- state: string;
332
- draft: boolean;
333
- mergeable: boolean | null;
334
- head: { ref: string; sha: string };
335
- base: { ref: string };
336
- };
337
-
338
- function isRestPullRequestCiStatusResponse(
339
- value: unknown,
340
- ): value is RestPullRequestCiStatusResponse {
341
- if (!isRecord(value)) return false;
342
- const head: unknown = value.head;
343
- const base: unknown = value.base;
344
- return (
345
- typeof value.html_url === 'string' &&
346
- typeof value.state === 'string' &&
347
- typeof value.draft === 'boolean' &&
348
- (value.mergeable === null || typeof value.mergeable === 'boolean') &&
349
- isRecord(head) &&
350
- typeof head.ref === 'string' &&
351
- typeof head.sha === 'string' &&
352
- isRecord(base) &&
353
- typeof base.ref === 'string'
354
- );
355
- }
356
-
357
- export const graphqlMergeableFromRestMergeable = (
358
- mergeable: boolean | null,
359
- ): string => {
360
- if (mergeable === true) {
361
- return 'MERGEABLE';
362
- }
363
- if (mergeable === false) {
364
- return 'CONFLICTING';
365
- }
366
- return 'UNKNOWN';
367
- };
368
-
369
- function isNullableString(value: unknown): value is string | null {
370
- return value === null || typeof value === 'string';
371
- }
372
-
373
- function isLoginContainer(value: unknown): value is { login: string } {
374
- return isRecord(value) && typeof value.login === 'string';
375
- }
376
-
377
- function isRefContainer(value: unknown): value is { ref: string } {
378
- return isRecord(value) && typeof value.ref === 'string';
379
- }
380
-
381
- function isRepoMergeSettings(
382
- value: unknown,
383
- ): value is { allow_squash_merge?: boolean; allow_rebase_merge?: boolean } {
384
- return isRecord(value);
385
- }
386
-
387
- type IssueOrPullRequestBodyResponse = {
388
- body: string | null;
389
- };
390
-
391
- function isIssueOrPullRequestBodyResponse(
392
- value: unknown,
393
- ): value is IssueOrPullRequestBodyResponse {
394
- return isRecord(value) && isNullableString(value.body);
395
- }
396
-
397
- type IssueOrPullRequestStateResponse = {
398
- state: string;
399
- title: string;
400
- };
401
-
402
- function isIssueOrPullRequestStateResponse(
403
- value: unknown,
404
- ): value is IssueOrPullRequestStateResponse {
405
- return (
406
- isRecord(value) &&
407
- typeof value.state === 'string' &&
408
- typeof value.title === 'string'
409
- );
410
- }
411
-
412
- type IssueCommentsResponseItem = {
413
- user: { login: string } | null;
414
- body: string | null;
415
- created_at: string;
416
- };
417
-
418
- function isIssueCommentsResponseItem(
419
- value: unknown,
420
- ): value is IssueCommentsResponseItem {
421
- if (!isRecord(value)) return false;
422
- const userValid = value.user === null || isLoginContainer(value.user);
423
- return (
424
- userValid &&
425
- isNullableString(value.body) &&
426
- typeof value.created_at === 'string'
427
- );
428
- }
429
-
430
- function isIssueCommentsResponse(
431
- value: unknown,
432
- ): value is IssueCommentsResponseItem[] {
433
- return Array.isArray(value) && value.every(isIssueCommentsResponseItem);
434
- }
435
-
436
- type IssueCommentIdResponseItem = {
437
- id: number;
438
- };
439
-
440
- function isIssueCommentIdResponseItem(
441
- value: unknown,
442
- ): value is IssueCommentIdResponseItem {
443
- return isRecord(value) && typeof value.id === 'number';
444
- }
445
-
446
- function isIssueCommentIdResponse(
447
- value: unknown,
448
- ): value is IssueCommentIdResponseItem[] {
449
- return Array.isArray(value) && value.every(isIssueCommentIdResponseItem);
450
- }
451
-
452
- type PullRequestDetailResponse = {
453
- title: string;
454
- state: string;
455
- merged: boolean;
456
- draft: boolean;
457
- additions: number;
458
- deletions: number;
459
- changed_files: number;
460
- head: { ref: string };
461
- base: { ref: string };
462
- user: { login: string } | null;
463
- body: string | null;
464
- };
465
-
466
- function isPullRequestDetailResponse(
467
- value: unknown,
468
- ): value is PullRequestDetailResponse {
469
- if (!isRecord(value)) return false;
470
- const userValid = value.user === null || isLoginContainer(value.user);
471
- return (
472
- typeof value.title === 'string' &&
473
- typeof value.state === 'string' &&
474
- typeof value.merged === 'boolean' &&
475
- typeof value.draft === 'boolean' &&
476
- typeof value.additions === 'number' &&
477
- typeof value.deletions === 'number' &&
478
- typeof value.changed_files === 'number' &&
479
- isRefContainer(value.head) &&
480
- isRefContainer(value.base) &&
481
- userValid &&
482
- isNullableString(value.body)
483
- );
484
- }
485
-
486
- type PullRequestDetailFilesResponseItem = {
487
- filename: string;
488
- status: string;
489
- additions: number;
490
- deletions: number;
491
- patch?: string;
492
- raw_url?: string;
493
- };
494
-
495
- function isPullRequestDetailFilesResponseItem(
496
- value: unknown,
497
- ): value is PullRequestDetailFilesResponseItem {
498
- if (!isRecord(value)) return false;
499
- return (
500
- typeof value.filename === 'string' &&
501
- typeof value.status === 'string' &&
502
- typeof value.additions === 'number' &&
503
- typeof value.deletions === 'number' &&
504
- (value.patch === undefined || typeof value.patch === 'string') &&
505
- (value.raw_url === undefined || typeof value.raw_url === 'string')
506
- );
507
- }
508
-
509
- function isPullRequestDetailFilesResponse(
510
- value: unknown,
511
- ): value is PullRequestDetailFilesResponseItem[] {
512
- return (
513
- Array.isArray(value) && value.every(isPullRequestDetailFilesResponseItem)
514
- );
515
- }
516
-
517
- type PullRequestCommitsResponseItem = {
518
- sha: string;
519
- commit: {
520
- message: string;
521
- author: { name: string; date: string } | null;
522
- };
523
- };
524
-
525
- function isCommitAuthor(
526
- value: unknown,
527
- ): value is { name: string; date: string } {
528
- return (
529
- isRecord(value) &&
530
- typeof value.name === 'string' &&
531
- typeof value.date === 'string'
532
- );
533
- }
534
-
535
- function isPullRequestCommitsResponseItem(
536
- value: unknown,
537
- ): value is PullRequestCommitsResponseItem {
538
- if (!isRecord(value)) return false;
539
- if (typeof value.sha !== 'string') return false;
540
- if (!isRecord(value.commit)) return false;
541
- if (typeof value.commit.message !== 'string') return false;
542
- return value.commit.author === null || isCommitAuthor(value.commit.author);
543
- }
544
-
545
- function isPullRequestCommitsResponse(
546
- value: unknown,
547
- ): value is PullRequestCommitsResponseItem[] {
548
- return Array.isArray(value) && value.every(isPullRequestCommitsResponseItem);
549
- }
550
-
551
- export class ApiV3CheerioRestIssueRepository
552
- extends BaseGitHubRepository
553
- implements IssueRepository
554
- {
555
- constructor(
556
- readonly apiV3IssueRepository: Pick<ApiV3IssueRepository, 'searchIssue'>,
557
- readonly restIssueRepository: Pick<
558
- RestIssueRepository,
559
- | 'createNewIssue'
560
- | 'updateIssue'
561
- | 'updateIssueBody'
562
- | 'createComment'
563
- | 'getIssue'
564
- | 'updateLabels'
565
- | 'removeLabel'
566
- | 'updateAssigneeList'
567
- | 'searchIssues'
568
- | 'getOrCreateLabel'
569
- >,
570
- readonly graphqlProjectItemRepository: Pick<
571
- GraphqlProjectItemRepository,
572
- | 'fetchProjectItems'
573
- | 'fetchProjectItemsLight'
574
- | 'fetchProjectItemsByIds'
575
- | 'fetchProjectItemByUrl'
576
- | 'updateProjectField'
577
- | 'clearProjectField'
578
- | 'updateProjectTextField'
579
- | 'addIssueToProject'
580
- >,
581
- readonly localStorageCacheRepository: Pick<
582
- LocalStorageCacheRepository,
583
- 'getSingle' | 'setSingle'
584
- >,
585
- readonly projectRepository: Pick<ProjectRepository, 'getProject'>,
586
- readonly dateRepository: DateRepository,
587
- readonly localStorageRepository: LocalStorageRepository,
588
- readonly ghToken: string = process.env.GH_TOKEN || 'dummy',
589
- readonly sleep: Sleep = realSleep,
590
- ) {
591
- super(localStorageRepository, ghToken);
592
- this.projectIssuesCacheRepository = new ProjectIssuesCacheRepository(
593
- localStorageCacheRepository,
594
- );
595
- }
596
-
597
- private readonly projectIssuesCacheRepository: ProjectIssuesCacheRepository;
598
-
599
- private readonly getAllIssuesRefreshMemo = new Map<
600
- Project['id'],
601
- { issues: Issue[]; project: Project; cacheUsed: boolean }
602
- >();
603
-
604
- private readonly lastIssuesFetchedAtByProjectId = new Map<
605
- Project['id'],
606
- string
607
- >();
608
-
609
- getLastIssuesFetchedAt = (projectId: Project['id']): string | null =>
610
- this.lastIssuesFetchedAtByProjectId.get(projectId) ?? null;
611
-
612
- private fetchWithRateLimitRetry = (
613
- request: () => Promise<Response>,
614
- ): Promise<Response> => fetchWithGitHubRateLimitRetry(request, this.sleep);
615
-
616
- private throwGitHubError = async (
617
- prefix: string,
618
- response: Response,
619
- ): Promise<never> => {
620
- const bodyText = await response.clone().text();
621
- const isRateLimit = hasRateLimitSignals(
622
- response.status,
623
- response.headers,
624
- bodyText,
625
- );
626
- const formatted = await this.formatGitHubErrorWithStatus(response);
627
- const message = `${prefix}: ${formatted}`;
628
- if (isRateLimit) {
629
- throw new GitHubRateLimitError(message);
630
- }
631
- throw new Error(message);
632
- };
633
-
634
- updateStatus: (
635
- project: Project,
636
- issue: Issue,
637
- statusId: string,
638
- ) => Promise<void> = async (project, issue, statusId) => {
639
- await this.graphqlProjectItemRepository.updateProjectField(
640
- project.id,
641
- project.status.fieldId,
642
- issue.itemId,
643
- { singleSelectOptionId: statusId },
644
- );
645
- };
646
-
647
- convertProjectItemToIssue = (item: ProjectItem): Issue => {
648
- const nextActionDate = item.customFields.find(
649
- (field) => normalizeFieldName(field.name) === 'nextactiondate',
650
- )?.value;
651
- const nextActionHour = item.customFields.find(
652
- (field) => normalizeFieldName(field.name) === 'nextactionhour',
653
- )?.value;
654
- const estimationMinutes = item.customFields.find(
655
- (field) => normalizeFieldName(field.name) === 'estimationminutes',
656
- )?.value;
657
- const dependedIssueUrls =
658
- item.customFields
659
- .find((field) =>
660
- normalizeFieldName(field.name).startsWith('dependedissueurls'),
661
- )
662
- ?.value?.split(',')
663
- .map((url) => url.trim())
664
- .filter((url) => url.length > 0) || [];
665
- const completionDate50PercentConfidence = item.customFields.find((field) =>
666
- normalizeFieldName(field.name).startsWith('completiondate50'),
667
- )?.value;
668
- const story = item.customFields.find(
669
- (field) => normalizeFieldName(field.name) === 'story',
670
- )?.value;
671
- const status = item.customFields.find(
672
- (field) => normalizeFieldName(field.name) === 'status',
673
- )?.value;
674
- const agent =
675
- item.customFields.find(
676
- (field) => normalizeFieldName(field.name) === 'agent',
677
- )?.value ?? null;
678
- const { owner, repo } = this.extractIssueFromUrl(item.url);
679
-
680
- return {
681
- nameWithOwner: item.nameWithOwner,
682
- url: item.url,
683
- title: item.title,
684
- number: item.number,
685
- state: item.state,
686
- labels: item.labels,
687
- assignees: item.assignees,
688
- nextActionDate: nextActionDate ? new Date(nextActionDate) : null,
689
- nextActionHour: nextActionHour ? parseInt(nextActionHour) : null,
690
- estimationMinutes: estimationMinutes ? parseInt(estimationMinutes) : null,
691
- dependedIssueUrls: dependedIssueUrls,
692
- completionDate50PercentConfidence: completionDate50PercentConfidence
693
- ? new Date(completionDate50PercentConfidence)
694
- : null,
695
- status: status || null,
696
- story: story || null,
697
- org: owner,
698
- repo: repo,
699
- body: item.body ?? '',
700
- itemId: item.id,
701
- isPr: item.url.includes('/pull/'),
702
- isInProgress: normalizeFieldName(status || '').includes('progress'),
703
- isClosed: item.state !== 'OPEN',
704
- createdAt: new Date(item.createdAt || '2000-01-01'),
705
- author: item.author,
706
- closingIssueReferenceUrls: item.closingIssueReferenceUrls,
707
- agent,
708
- isRepoArchived: item.isRepoArchived,
709
- stateReason: item.stateReason,
710
- };
711
- };
712
- private restoreIssuesFromCache = (rawIssues: unknown): Issue[] | null => {
713
- if (!Array.isArray(rawIssues)) {
714
- return null;
715
- }
716
- const issues = rawIssues
717
- .filter(
718
- (issue: unknown): issue is object =>
719
- typeof issue === 'object' && issue !== null,
720
- )
721
- .map((issue: object): object => {
722
- const nextActionDate =
723
- !('nextActionDate' in issue) ||
724
- typeof issue.nextActionDate !== 'string' ||
725
- issue.nextActionDate === null
726
- ? null
727
- : new Date(issue.nextActionDate);
728
- const completionDate50PercentConfidence =
729
- !('completionDate50PercentConfidence' in issue) ||
730
- typeof issue.completionDate50PercentConfidence !== 'string'
731
- ? null
732
- : new Date(issue.completionDate50PercentConfidence);
733
- const createdAt =
734
- !('createdAt' in issue) || typeof issue.createdAt !== 'string'
735
- ? new Date()
736
- : new Date(issue.createdAt);
737
- const closingIssueReferenceUrls =
738
- 'closingIssueReferenceUrls' in issue &&
739
- Array.isArray(issue.closingIssueReferenceUrls) &&
740
- issue.closingIssueReferenceUrls.every(
741
- (url): url is string => typeof url === 'string',
742
- )
743
- ? issue.closingIssueReferenceUrls
744
- : [];
745
-
746
- const stateReason =
747
- 'stateReason' in issue &&
748
- (issue.stateReason === 'COMPLETED' ||
749
- issue.stateReason === 'NOT_PLANNED' ||
750
- issue.stateReason === 'REOPENED')
751
- ? issue.stateReason
752
- : null;
753
- return {
754
- ...issue,
755
- nextActionDate: nextActionDate,
756
- completionDate50PercentConfidence: completionDate50PercentConfidence,
757
- createdAt: createdAt,
758
- closingIssueReferenceUrls: closingIssueReferenceUrls,
759
- stateReason: stateReason,
760
- };
761
- });
762
-
763
- if (isIssueArray(issues)) {
764
- return issues;
765
- }
766
- return null;
767
- };
768
-
769
- private readCachedProjectIssues = async (
770
- projectId: Project['id'],
771
- ): Promise<CachedProjectIssues | null> => {
772
- const raw = await this.projectIssuesCacheRepository.readRaw(projectId);
773
- if (typeof raw !== 'object' || raw === null) {
774
- return null;
775
- }
776
- if (
777
- !('lastFetchedAt' in raw) ||
778
- typeof raw.lastFetchedAt !== 'string' ||
779
- !('lastFullFetchAt' in raw) ||
780
- typeof raw.lastFullFetchAt !== 'string' ||
781
- !('project' in raw) ||
782
- !('issues' in raw)
783
- ) {
784
- return null;
785
- }
786
- if (!isProject(raw.project)) {
787
- return null;
788
- }
789
- const issues = this.restoreIssuesFromCache(raw.issues);
790
- if (!issues) {
791
- return null;
792
- }
793
- return {
794
- lastFetchedAt: raw.lastFetchedAt,
795
- lastFullFetchAt: raw.lastFullFetchAt,
796
- project: raw.project,
797
- issues,
798
- };
799
- };
800
-
801
- // Reads the Project (status/story option ids and field ids) that the TDPM
802
- // daemon persisted into the `allIssues-${projectId}` cache, without any
803
- // GraphQL call. Returns null on cache miss so callers can fall back to a
804
- // GraphQL project load only when the daemon has not populated the cache yet.
805
- getCachedProject = async (
806
- projectId: Project['id'],
807
- ): Promise<Project | null> =>
808
- this.projectIssuesCacheRepository.readProject(projectId);
809
-
810
- private toDateString = (date: Date): string =>
811
- `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, '0')}-${String(date.getUTCDate()).padStart(2, '0')}`;
812
-
813
- getAllIssues = async (
814
- projectId: Project['id'],
815
- ): Promise<{
816
- issues: Issue[];
817
- project: Project;
818
- cacheUsed: boolean;
819
- }> => {
820
- const memoized = this.getAllIssuesRefreshMemo.get(projectId);
821
- if (memoized) {
822
- return memoized;
823
- }
824
- const result = await this.refreshAllIssues(projectId);
825
- this.getAllIssuesRefreshMemo.set(projectId, result);
826
- return result;
827
- };
828
-
829
- private refreshAllIssues = async (
830
- projectId: Project['id'],
831
- ): Promise<{ issues: Issue[]; project: Project; cacheUsed: boolean }> => {
832
- const now = await this.dateRepository.now();
833
- const cache = await this.readCachedProjectIssues(projectId);
834
- const isFullFetch =
835
- cache === null ||
836
- now.getTime() - new Date(cache.lastFullFetchAt).getTime() >=
837
- FULL_ISSUE_FETCH_INTERVAL_MS;
838
-
839
- let project: Project;
840
- try {
841
- const freshProject = await this.projectRepository.getProject(projectId);
842
- if (!freshProject) {
843
- throw new Error(`Project not found. projectId: ${projectId}`);
844
- }
845
- project = freshProject;
846
- } catch (error) {
847
- if (!isFullFetch && cache !== null) {
848
- console.warn(
849
- `Failed to refresh project metadata, using cached. projectId: ${projectId}, error: ${String(error)}`,
850
- );
851
- project = cache.project;
852
- } else {
853
- throw error;
854
- }
855
- }
856
-
857
- const cacheStoriesByOptionId = new Map(
858
- cache?.project.story?.stories.map((s) => [s.id, s.name]) ?? [],
859
- );
860
- const freshStoryOptionIds = new Set(
861
- project.story?.stories.map((s) => s.id) ?? [],
862
- );
863
- const storyOptionsChanged =
864
- cache !== null &&
865
- ([...cacheStoriesByOptionId.entries()].some(
866
- ([id, name]) =>
867
- !freshStoryOptionIds.has(id) ||
868
- project.story?.stories.find((s) => s.id === id)?.name !== name,
869
- ) ??
870
- false);
871
- const effectiveIsFullFetch = isFullFetch || storyOptionsChanged;
872
-
873
- if (effectiveIsFullFetch) {
874
- const items =
875
- await this.graphqlProjectItemRepository.fetchProjectItems(projectId);
876
- const issues = items.map((item) => this.convertProjectItemToIssue(item));
877
- const nowIso = now.toISOString();
878
- await this.projectIssuesCacheRepository.write(projectId, {
879
- lastFetchedAt: nowIso,
880
- lastFullFetchAt: nowIso,
881
- project,
882
- issues,
883
- });
884
- this.lastIssuesFetchedAtByProjectId.set(projectId, nowIso);
885
- return { issues, project, cacheUsed: false };
886
- }
887
-
888
- const lastFetchedAt = new Date(cache.lastFetchedAt);
889
- const cutoff = new Date(
890
- lastFetchedAt.getTime() - INCREMENTAL_FETCH_SKEW_BUFFER_MS,
891
- );
892
- const lightItems =
893
- await this.graphqlProjectItemRepository.fetchProjectItemsLight(
894
- projectId,
895
- `updated:>=${this.toDateString(cutoff)}`,
896
- );
897
- const changedItemIds = lightItems
898
- .filter((item) => new Date(item.updatedAt).getTime() >= cutoff.getTime())
899
- .map((item) => item.id);
900
- const issuesByUrl = new Map<string, Issue>(
901
- cache.issues.map((issue) => [issue.url, issue]),
902
- );
903
- if (changedItemIds.length > 0) {
904
- const changedItems =
905
- await this.graphqlProjectItemRepository.fetchProjectItemsByIds(
906
- changedItemIds,
907
- );
908
- for (const item of changedItems) {
909
- const issue = this.convertProjectItemToIssue(item);
910
- issuesByUrl.set(issue.url, issue);
911
- }
912
- }
913
- const issues = Array.from(issuesByUrl.values());
914
- const nowIso = now.toISOString();
915
- await this.projectIssuesCacheRepository.write(projectId, {
916
- lastFetchedAt: nowIso,
917
- lastFullFetchAt: cache.lastFullFetchAt,
918
- project,
919
- issues,
920
- });
921
- this.lastIssuesFetchedAtByProjectId.set(projectId, nowIso);
922
- return { issues, project, cacheUsed: true };
923
- };
924
- createNewIssue = async (
925
- org: string,
926
- repo: string,
927
- title: string,
928
- body: string,
929
- assignees: string[],
930
- labels: string[],
931
- ): Promise<number> => {
932
- return await this.restIssueRepository.createNewIssue(
933
- org,
934
- repo,
935
- title,
936
- body,
937
- assignees,
938
- labels,
939
- );
940
- };
941
- searchIssue = async (query: {
942
- owner: string;
943
- repositoryName: string;
944
- type?: 'issue' | 'pr';
945
- state?: 'open' | 'closed' | 'all';
946
- title?: string;
947
- createdFrom?: string;
948
- assignee?: string;
949
- }): Promise<
950
- {
951
- url: string;
952
- title: string;
953
- number: string;
954
- }[]
955
- > => {
956
- return await this.apiV3IssueRepository.searchIssue(query);
957
- };
958
- updateIssue = async (issue: Issue): Promise<void> => {
959
- await this.restIssueRepository.updateIssue(issue);
960
- };
961
- updateIssueBody = async (
962
- issue: Pick<Issue, 'org' | 'repo' | 'number'>,
963
- body: string,
964
- ): Promise<void> => {
965
- await this.restIssueRepository.updateIssueBody(issue, body);
966
- };
967
- getIssueByUrl = async (url: string): Promise<Issue | null> => {
968
- const projectItem =
969
- await this.graphqlProjectItemRepository.fetchProjectItemByUrl(url);
970
- if (!projectItem) {
971
- return null;
972
- }
973
- return this.convertProjectItemToIssue(projectItem);
974
- };
975
- addIssueToProject = async (
976
- project: Project,
977
- issueUrl: string,
978
- ): Promise<void> => {
979
- await this.graphqlProjectItemRepository.addIssueToProject(
980
- project.id,
981
- issueUrl,
982
- );
983
- };
984
- setDependedIssueUrl = async (
985
- prUrl: string,
986
- project: Project,
987
- issueUrl: string,
988
- ): Promise<void> => {
989
- const dependedIssueUrlField = project.dependedIssueUrlSeparatedByComma;
990
- if (!dependedIssueUrlField) {
991
- return;
992
- }
993
- const existingProjectItem =
994
- await this.graphqlProjectItemRepository.fetchProjectItemByUrl(
995
- prUrl,
996
- project.id,
997
- );
998
- const existingValue = existingProjectItem?.customFields.find(
999
- (field) => field.name === dependedIssueUrlField.name,
1000
- )?.value;
1001
- if (existingValue) {
1002
- return;
1003
- }
1004
- const projectItemId =
1005
- existingProjectItem?.id ??
1006
- (await this.graphqlProjectItemRepository.addIssueToProject(
1007
- project.id,
1008
- prUrl,
1009
- ));
1010
- await this.graphqlProjectItemRepository.updateProjectTextField(
1011
- project.id,
1012
- dependedIssueUrlField.fieldId,
1013
- projectItemId,
1014
- issueUrl,
1015
- );
1016
- };
1017
-
1018
- setIssueAgentField = async (
1019
- issueUrl: string,
1020
- project: Project,
1021
- agentOptionId: string,
1022
- ): Promise<void> => {
1023
- if (!project.agent) {
1024
- return;
1025
- }
1026
- const existingProjectItem =
1027
- await this.graphqlProjectItemRepository.fetchProjectItemByUrl(
1028
- issueUrl,
1029
- project.id,
1030
- );
1031
- const projectItemId =
1032
- existingProjectItem?.id ??
1033
- (await this.graphqlProjectItemRepository.addIssueToProject(
1034
- project.id,
1035
- issueUrl,
1036
- ));
1037
- await this.graphqlProjectItemRepository.updateProjectField(
1038
- project.id,
1039
- project.agent.fieldId,
1040
- projectItemId,
1041
- { singleSelectOptionId: agentOptionId },
1042
- );
1043
- };
1044
-
1045
- updateNextActionDate = async (
1046
- issueUrl: string,
1047
- project: Project,
1048
- date: Date,
1049
- projectItemId?: string,
1050
- ): Promise<void> => {
1051
- if (!project.nextActionDate) {
1052
- return;
1053
- }
1054
- // When the caller already knows the project item id (e.g. the console,
1055
- // which receives it in the request body), use it directly and skip the
1056
- // GraphQL fetchProjectItemByUrl lookup. Fall back to the lookup only when
1057
- // no id was supplied, preserving the original behavior for other callers.
1058
- const itemId =
1059
- projectItemId ??
1060
- (
1061
- await this.graphqlProjectItemRepository.fetchProjectItemByUrl(
1062
- issueUrl,
1063
- project.id,
1064
- )
1065
- )?.id;
1066
- if (!itemId) {
1067
- return;
1068
- }
1069
- return this.graphqlProjectItemRepository.updateProjectField(
1070
- project.id,
1071
- project.nextActionDate.fieldId,
1072
- itemId,
1073
- { date: date.toISOString().split('T')[0] },
1074
- );
1075
- };
1076
- updateNextActionHour = async (
1077
- project: Project & {
1078
- nextActionHour: NonNullable<Project['nextActionHour']>;
1079
- },
1080
- issue: Issue,
1081
- hour: number,
1082
- ): Promise<void> => {
1083
- const option = project.nextActionHour.options.find(
1084
- (o) => o.name === String(hour),
1085
- );
1086
- const value = option
1087
- ? { singleSelectOptionId: option.id }
1088
- : { number: hour };
1089
- return this.graphqlProjectItemRepository.updateProjectField(
1090
- project.id,
1091
- project.nextActionHour.fieldId,
1092
- issue.itemId,
1093
- value,
1094
- );
1095
- };
1096
- updateStory = async (
1097
- project: Project & { story: NonNullable<Project['story']> },
1098
- issue: Issue,
1099
- storyOptionId: string,
1100
- ): Promise<void> => {
1101
- await this.graphqlProjectItemRepository.updateProjectField(
1102
- project.id,
1103
- project.story.fieldId,
1104
- issue.itemId,
1105
- { singleSelectOptionId: storyOptionId },
1106
- );
1107
- };
1108
- updateStoryOptionColor = async (
1109
- project: Project & { story: NonNullable<Project['story']> },
1110
- storyOptionId: string,
1111
- newColor: FieldOption['color'],
1112
- ): Promise<void> => {
1113
- const mutation = `mutation UpdateStoryOptionColor($fieldId: ID!, $options: [ProjectV2SingleSelectFieldOptionInput!]!) {
1114
- updateProjectV2Field(input: {
1115
- fieldId: $fieldId
1116
- singleSelectOptions: $options
1117
- }) {
1118
- projectV2Field {
1119
- ... on ProjectV2SingleSelectField {
1120
- options { id name color description }
1121
- }
1122
- }
1123
- }
1124
- }`;
1125
- const options = project.story.stories.map((opt) => ({
1126
- id: opt.id,
1127
- name: opt.name,
1128
- color: opt.id === storyOptionId ? newColor : opt.color,
1129
- description: opt.description,
1130
- }));
1131
- const response = await postGithubGraphqlJson<{
1132
- errors?: { message: string }[];
1133
- }>({
1134
- ghToken: this.ghToken,
1135
- query: mutation,
1136
- variables: { fieldId: project.story.fieldId, options },
1137
- });
1138
- if (response.errors && response.errors.length > 0) {
1139
- throw new Error(response.errors.map((e) => e.message).join('; '));
1140
- }
1141
- };
1142
- clearProjectField = async (
1143
- project: Project,
1144
- fieldId: string,
1145
- issue: Issue,
1146
- ): Promise<void> => {
1147
- await this.graphqlProjectItemRepository.clearProjectField(
1148
- project.id,
1149
- fieldId,
1150
- issue.itemId,
1151
- );
1152
- return;
1153
- };
1154
- createComment = async (issue: Issue, comment: string): Promise<void> => {
1155
- await this.restIssueRepository.createComment(issue.url, comment);
1156
- };
1157
- updateProjectTextField = async (
1158
- project: Project,
1159
- fieldId: string,
1160
- issue: Issue,
1161
- text: string,
1162
- ): Promise<void> => {
1163
- await this.graphqlProjectItemRepository.updateProjectTextField(
1164
- project.id,
1165
- fieldId,
1166
- issue.itemId,
1167
- text,
1168
- );
1169
- };
1170
-
1171
- updateLabels = (issue: Issue, labels: Issue['labels']): Promise<void> => {
1172
- return this.restIssueRepository.updateLabels(issue, labels);
1173
- };
1174
- removeLabel = (issue: Issue, label: string): Promise<void> => {
1175
- return this.restIssueRepository.removeLabel(issue, label);
1176
- };
1177
- getOrCreateLabel = (
1178
- org: string,
1179
- repo: string,
1180
- labelName: string,
1181
- ): Promise<void> => {
1182
- return this.restIssueRepository.getOrCreateLabel(org, repo, labelName);
1183
- };
1184
- updateAssigneeList = (
1185
- issue: Pick<Issue, 'org' | 'repo' | 'number'>,
1186
- assigneeList: Member['name'][],
1187
- ): Promise<void> => {
1188
- return this.restIssueRepository.updateAssigneeList(issue, assigneeList);
1189
- };
1190
- searchIssues = (query: string): Promise<SearchedIssue[]> => {
1191
- return this.restIssueRepository.searchIssues(query);
1192
- };
1193
- get = async (issueUrl: string, project: Project): Promise<Issue | null> => {
1194
- const projectItem =
1195
- await this.graphqlProjectItemRepository.fetchProjectItemByUrl(
1196
- issueUrl,
1197
- project.id,
1198
- );
1199
- if (!projectItem) {
1200
- return null;
1201
- }
1202
- return this.convertProjectItemToIssue(projectItem);
1203
- };
1204
- update = async (issue: Issue, _project: Project): Promise<void> => {
1205
- await this.updateIssue(issue);
1206
- };
1207
- private parseIssueUrl = (
1208
- issueUrl: string,
1209
- ): {
1210
- owner: string;
1211
- repo: string;
1212
- issueNumber: number;
1213
- isPr: boolean;
1214
- } => {
1215
- const urlMatch = issueUrl.match(
1216
- /github\.com\/([^/]+)\/([^/]+)\/(issues|pull)\/(\d+)/,
1217
- );
1218
- if (!urlMatch) {
1219
- throw new Error(`Invalid GitHub issue URL: ${issueUrl}`);
1220
- }
1221
- return {
1222
- owner: urlMatch[1],
1223
- repo: urlMatch[2],
1224
- issueNumber: parseInt(urlMatch[4], 10),
1225
- isPr: urlMatch[3] === 'pull',
1226
- };
1227
- };
1228
-
1229
- private computePrStatus = (
1230
- prUrl: string,
1231
- headRefName: string | undefined,
1232
- data: PrStatusComputationData,
1233
- ): RelatedPullRequest => {
1234
- const isConflicted = data.mergeable === 'CONFLICTING';
1235
- const hasStatusCheckRollup = data.ciContexts.length > 0;
1236
- const contexts = data.ciContexts;
1237
-
1238
- const requiredCheckNames = data.requiredCheckNames;
1239
- const seenContextNames = new Set<string>();
1240
- for (const ctx of contexts) {
1241
- if ('name' in ctx) {
1242
- seenContextNames.add(ctx.name);
1243
- }
1244
- if ('context' in ctx) {
1245
- seenContextNames.add(ctx.context);
1246
- }
1247
- }
1248
-
1249
- const missingRequiredCheckNames = requiredCheckNames.filter(
1250
- (name) => !seenContextNames.has(name),
1251
- );
1252
- const allRequiredChecksPassed = missingRequiredCheckNames.length === 0;
1253
-
1254
- const latestCheckRunByName = new Map<
1255
- string,
1256
- { conclusion: string | null; databaseId: number }
1257
- >();
1258
- for (const ctx of contexts) {
1259
- if (ctx.__typename === 'CheckRun') {
1260
- const existing = latestCheckRunByName.get(ctx.name);
1261
- if (!existing || ctx.databaseId > existing.databaseId) {
1262
- latestCheckRunByName.set(ctx.name, {
1263
- conclusion: ctx.conclusion,
1264
- databaseId: ctx.databaseId,
1265
- });
1266
- }
1267
- }
1268
- }
1269
- const failureConclusions = new Set([
1270
- 'FAILURE',
1271
- 'CANCELLED',
1272
- 'TIMED_OUT',
1273
- 'ACTION_REQUIRED',
1274
- 'STARTUP_FAILURE',
1275
- 'STALE',
1276
- ]);
1277
- const isCiStateSuccess = (() => {
1278
- if (!hasStatusCheckRollup) return false;
1279
- const latestRuns = [...latestCheckRunByName.values()];
1280
- const statusContexts = contexts.filter(
1281
- (
1282
- ctx,
1283
- ): ctx is {
1284
- __typename: 'StatusContext';
1285
- context: string;
1286
- state: string;
1287
- } => ctx.__typename === 'StatusContext',
1288
- );
1289
- const hasFailure =
1290
- latestRuns.some(
1291
- (r) => r.conclusion !== null && failureConclusions.has(r.conclusion),
1292
- ) ||
1293
- statusContexts.some(
1294
- (ctx) => ctx.state === 'FAILURE' || ctx.state === 'ERROR',
1295
- );
1296
- if (hasFailure) return false;
1297
- const hasPending =
1298
- latestRuns.some((r) => r.conclusion === null) ||
1299
- statusContexts.some((ctx) => ctx.state === 'PENDING');
1300
- return !hasPending;
1301
- })();
1302
- const isPassedAllCiJob = isCiStateSuccess && allRequiredChecksPassed;
1303
-
1304
- const reviewThreads = data.reviewThreads;
1305
- const isResolvedAllReviewComments =
1306
- reviewThreads.length === 0 ||
1307
- reviewThreads.every((thread) => thread.isResolved);
1308
-
1309
- return {
1310
- url: prUrl,
1311
- branchName: headRefName ?? null,
1312
- createdAt: new Date(0),
1313
- isDraft: data.isDraft === true,
1314
- isConflicted,
1315
- mergeable: data.mergeable ?? null,
1316
- isPassedAllCiJob,
1317
- isCiStateSuccess,
1318
- isResolvedAllReviewComments,
1319
- isBranchOutOfDate: false,
1320
- missingRequiredCheckNames,
1321
- };
1322
- };
1323
-
1324
- private prBodyContainsCrossRepoClosingKeyword = (
1325
- prBody: string | null,
1326
- issueUrl: string,
1327
- ): boolean => {
1328
- if (!prBody) return false;
1329
- const closingKeywords =
1330
- /(?:close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\s+/gi;
1331
- const normalizedIssueUrl = issueUrl.replace(/\/+$/, '');
1332
- let match: RegExpExecArray | null;
1333
- while ((match = closingKeywords.exec(prBody)) !== null) {
1334
- const afterKeyword = prBody.slice(match.index + match[0].length);
1335
- const urlMatch = afterKeyword.match(/^https?:\/\/[^\s]+/);
1336
- if (!urlMatch) continue;
1337
- const candidateUrl = urlMatch[0].replace(/\/+$/, '');
1338
- if (candidateUrl.toLowerCase() === normalizedIssueUrl.toLowerCase())
1339
- return true;
1340
- }
1341
- return false;
1342
- };
1343
-
1344
- private readonly requiredCheckNamesCache = new Map<
1345
- string,
1346
- { fetchedAtMs: number; names: string[] }
1347
- >();
1348
-
1349
- private getRequiredCheckNames = async (
1350
- owner: string,
1351
- repo: string,
1352
- branch: string,
1353
- ): Promise<string[]> => {
1354
- const cacheKey = `${owner}/${repo}/${branch}`;
1355
- const nowMs = (await this.dateRepository.now()).getTime();
1356
- const cached = this.requiredCheckNamesCache.get(cacheKey);
1357
- if (cached && nowMs - cached.fetchedAtMs < REQUIRED_CHECKS_CACHE_TTL_MS) {
1358
- return cached.names;
1359
- }
1360
-
1361
- const ownerSegment = encodeURIComponent(owner);
1362
- const repoSegment = encodeURIComponent(repo);
1363
- const branchSegment = encodeURIComponent(branch);
1364
- const requiredCheckNamesSet = new Set<string>();
1365
-
1366
- const rulesResponse = await this.fetchWithRateLimitRetry(() =>
1367
- fetch(
1368
- `https://api.github.com/repos/${ownerSegment}/${repoSegment}/rules/branches/${branchSegment}?per_page=100`,
1369
- {
1370
- method: 'GET',
1371
- headers: {
1372
- Authorization: `Bearer ${this.ghToken}`,
1373
- Accept: 'application/vnd.github+json',
1374
- },
1375
- },
1376
- ),
1377
- );
1378
- if (rulesResponse.ok) {
1379
- const rulesBody: unknown = await rulesResponse.json();
1380
- if (!isBranchRulesResponse(rulesBody)) {
1381
- throw new Error(
1382
- `Unexpected response shape when fetching branch rules: ${owner}/${repo}/${branch}`,
1383
- );
1384
- }
1385
- for (const rule of rulesBody) {
1386
- if (rule.type !== 'required_status_checks') continue;
1387
- for (const check of rule.parameters?.required_status_checks || []) {
1388
- requiredCheckNamesSet.add(check.context);
1389
- }
1390
- }
1391
- } else if (rulesResponse.status === 403) {
1392
- const reason = await this.formatGitHubErrorWithStatus(rulesResponse);
1393
- console.warn(
1394
- `ApiV3CheerioRestIssueRepository: branch rules are not accessible for ${owner}/${repo}/${branch}, treating as no required checks. reason: ${reason}`,
1395
- );
1396
- } else if (rulesResponse.status !== 404) {
1397
- const reason = await this.formatGitHubErrorWithStatus(rulesResponse);
1398
- throw new Error(
1399
- `Failed to fetch branch rules for ${owner}/${repo}/${branch}: ${reason}`,
1400
- );
1401
- }
1402
-
1403
- const branchResponse = await this.fetchWithRateLimitRetry(() =>
1404
- fetch(
1405
- `https://api.github.com/repos/${ownerSegment}/${repoSegment}/branches/${branchSegment}`,
1406
- {
1407
- method: 'GET',
1408
- headers: {
1409
- Authorization: `Bearer ${this.ghToken}`,
1410
- Accept: 'application/vnd.github+json',
1411
- },
1412
- },
1413
- ),
1414
- );
1415
- if (branchResponse.ok) {
1416
- const branchBody: unknown = await branchResponse.json();
1417
- if (!isBranchDetailResponse(branchBody)) {
1418
- throw new Error(
1419
- `Unexpected response shape when fetching branch detail: ${owner}/${repo}/${branch}`,
1420
- );
1421
- }
1422
- for (const context of branchBody.protection?.required_status_checks
1423
- ?.contexts || []) {
1424
- requiredCheckNamesSet.add(context);
1425
- }
1426
- } else if (branchResponse.status === 403) {
1427
- const reason = await this.formatGitHubErrorWithStatus(branchResponse);
1428
- console.warn(
1429
- `ApiV3CheerioRestIssueRepository: branch detail (classic protection) is not accessible for ${owner}/${repo}/${branch}, treating as no required checks. reason: ${reason}`,
1430
- );
1431
- } else if (branchResponse.status !== 404) {
1432
- const reason = await this.formatGitHubErrorWithStatus(branchResponse);
1433
- throw new Error(
1434
- `Failed to fetch branch detail for ${owner}/${repo}/${branch}: ${reason}`,
1435
- );
1436
- }
1437
-
1438
- const names = Array.from(requiredCheckNamesSet);
1439
- this.requiredCheckNamesCache.set(cacheKey, {
1440
- fetchedAtMs: nowMs,
1441
- names,
1442
- });
1443
- return names;
1444
- };
1445
-
1446
- private getCheckRunsViaCheckSuitesFallback = async (
1447
- owner: string,
1448
- repo: string,
1449
- commitSha: string,
1450
- ): Promise<CiContextNode[]> => {
1451
- const ownerSegment = encodeURIComponent(owner);
1452
- const repoSegment = encodeURIComponent(repo);
1453
- const shaSegment = encodeURIComponent(commitSha);
1454
-
1455
- const checkSuitesResponse = await this.fetchWithRateLimitRetry(() =>
1456
- fetch(
1457
- `https://api.github.com/repos/${ownerSegment}/${repoSegment}/commits/${shaSegment}/check-suites`,
1458
- {
1459
- method: 'GET',
1460
- headers: {
1461
- Authorization: `Bearer ${this.ghToken}`,
1462
- Accept: 'application/vnd.github+json',
1463
- },
1464
- },
1465
- ),
1466
- );
1467
-
1468
- if (!checkSuitesResponse.ok) {
1469
- if (checkSuitesResponse.status === 404) {
1470
- console.warn(
1471
- `ApiV3CheerioRestIssueRepository: commits/${commitSha}/check-suites returned 404 for ${owner}/${repo}, treating as no check runs.`,
1472
- );
1473
- return [];
1474
- }
1475
- const reason =
1476
- await this.formatGitHubErrorWithStatus(checkSuitesResponse);
1477
- throw new Error(
1478
- `Failed to fetch check suites for ${owner}/${repo}@${commitSha}: ${reason}`,
1479
- );
1480
- }
1481
-
1482
- const checkSuitesBody: unknown = await checkSuitesResponse.json();
1483
- if (!isCheckSuitesResponse(checkSuitesBody)) {
1484
- throw new Error(
1485
- `Unexpected response shape when fetching check suites: ${owner}/${repo}@${commitSha}`,
1486
- );
1487
- }
1488
-
1489
- const contexts: CiContextNode[] = [];
1490
- const perPage = 100;
1491
- for (const suite of checkSuitesBody.check_suites) {
1492
- let page = 1;
1493
- let hasMore = true;
1494
- while (hasMore) {
1495
- const runsResponse = await this.fetchWithRateLimitRetry(() =>
1496
- fetch(
1497
- `https://api.github.com/repos/${ownerSegment}/${repoSegment}/check-suites/${suite.id}/check-runs?per_page=${perPage}&page=${page}`,
1498
- {
1499
- method: 'GET',
1500
- headers: {
1501
- Authorization: `Bearer ${this.ghToken}`,
1502
- Accept: 'application/vnd.github+json',
1503
- },
1504
- },
1505
- ),
1506
- );
1507
- if (!runsResponse.ok) {
1508
- const reason = await this.formatGitHubErrorWithStatus(runsResponse);
1509
- throw new Error(
1510
- `Failed to fetch check runs for suite ${suite.id} in ${owner}/${repo}: ${reason}`,
1511
- );
1512
- }
1513
- const runsBody: unknown = await runsResponse.json();
1514
- if (!isCheckRunsResponse(runsBody)) {
1515
- throw new Error(
1516
- `Unexpected response shape when fetching check runs for suite ${suite.id}: ${owner}/${repo}@${commitSha}`,
1517
- );
1518
- }
1519
- for (const checkRun of runsBody.check_runs) {
1520
- contexts.push({
1521
- __typename: 'CheckRun',
1522
- name: checkRun.name,
1523
- conclusion: checkRun.conclusion
1524
- ? checkRun.conclusion.toUpperCase()
1525
- : null,
1526
- databaseId: checkRun.id,
1527
- });
1528
- }
1529
- if (
1530
- runsBody.check_runs.length < perPage ||
1531
- page * perPage >= runsBody.total_count
1532
- ) {
1533
- hasMore = false;
1534
- } else {
1535
- page += 1;
1536
- }
1537
- }
1538
- }
1539
- return contexts;
1540
- };
1541
-
1542
- private getCommitCiContexts = async (
1543
- owner: string,
1544
- repo: string,
1545
- commitSha: string,
1546
- ): Promise<CiContextNode[]> => {
1547
- const ownerSegment = encodeURIComponent(owner);
1548
- const repoSegment = encodeURIComponent(repo);
1549
- const shaSegment = encodeURIComponent(commitSha);
1550
- const contexts: CiContextNode[] = [];
1551
-
1552
- const perPage = 100;
1553
- let page = 1;
1554
- let hasMore = true;
1555
- let usedCheckSuitesFallback = false;
1556
- while (hasMore) {
1557
- const checkRunsResponse = await this.fetchWithRateLimitRetry(() =>
1558
- fetch(
1559
- `https://api.github.com/repos/${ownerSegment}/${repoSegment}/commits/${shaSegment}/check-runs?per_page=${perPage}&page=${page}`,
1560
- {
1561
- method: 'GET',
1562
- headers: {
1563
- Authorization: `Bearer ${this.ghToken}`,
1564
- Accept: 'application/vnd.github+json',
1565
- },
1566
- },
1567
- ),
1568
- );
1569
- if (!checkRunsResponse.ok) {
1570
- if (checkRunsResponse.status === 404 && page === 1) {
1571
- const fallbackContexts =
1572
- await this.getCheckRunsViaCheckSuitesFallback(
1573
- owner,
1574
- repo,
1575
- commitSha,
1576
- );
1577
- contexts.push(...fallbackContexts);
1578
- usedCheckSuitesFallback = true;
1579
- break;
1580
- }
1581
- const reason =
1582
- await this.formatGitHubErrorWithStatus(checkRunsResponse);
1583
- throw new Error(
1584
- `Failed to fetch check runs for ${owner}/${repo}@${commitSha}: ${reason}`,
1585
- );
1586
- }
1587
- const checkRunsBody: unknown = await checkRunsResponse.json();
1588
- if (!isCheckRunsResponse(checkRunsBody)) {
1589
- throw new Error(
1590
- `Unexpected response shape when fetching check runs: ${owner}/${repo}@${commitSha}`,
1591
- );
1592
- }
1593
- for (const checkRun of checkRunsBody.check_runs) {
1594
- contexts.push({
1595
- __typename: 'CheckRun',
1596
- name: checkRun.name,
1597
- conclusion: checkRun.conclusion
1598
- ? checkRun.conclusion.toUpperCase()
1599
- : null,
1600
- databaseId: checkRun.id,
1601
- });
1602
- }
1603
- if (
1604
- checkRunsBody.check_runs.length < perPage ||
1605
- page * perPage >= checkRunsBody.total_count
1606
- ) {
1607
- hasMore = false;
1608
- } else {
1609
- page += 1;
1610
- }
1611
- }
1612
-
1613
- if (!usedCheckSuitesFallback) {
1614
- const combinedStatusResponse = await this.fetchWithRateLimitRetry(() =>
1615
- fetch(
1616
- `https://api.github.com/repos/${ownerSegment}/${repoSegment}/commits/${shaSegment}/status?per_page=100`,
1617
- {
1618
- method: 'GET',
1619
- headers: {
1620
- Authorization: `Bearer ${this.ghToken}`,
1621
- Accept: 'application/vnd.github+json',
1622
- },
1623
- },
1624
- ),
1625
- );
1626
- if (!combinedStatusResponse.ok) {
1627
- const reason = await this.formatGitHubErrorWithStatus(
1628
- combinedStatusResponse,
1629
- );
1630
- throw new Error(
1631
- `Failed to fetch combined status for ${owner}/${repo}@${commitSha}: ${reason}`,
1632
- );
1633
- }
1634
- const combinedStatusBody: unknown = await combinedStatusResponse.json();
1635
- if (!isCombinedStatusResponse(combinedStatusBody)) {
1636
- throw new Error(
1637
- `Unexpected response shape when fetching combined status: ${owner}/${repo}@${commitSha}`,
1638
- );
1639
- }
1640
- for (const status of combinedStatusBody.statuses) {
1641
- contexts.push({
1642
- __typename: 'StatusContext',
1643
- context: status.context,
1644
- state: status.state.toUpperCase(),
1645
- });
1646
- }
1647
- }
1648
-
1649
- return contexts;
1650
- };
1651
-
1652
- private fetchSlimPullRequest = async (
1653
- owner: string,
1654
- repo: string,
1655
- prNumber: number,
1656
- ): Promise<SlimPullRequest | null> => {
1657
- const query = `
1658
- query PullRequestSlimStatus($owner: String!, $repo: String!, $prNumber: Int!, $reviewThreadsAfter: String) {
1659
- repository(owner: $owner, name: $repo) {
1660
- pullRequest(number: $prNumber) {
1661
- url
1662
- state
1663
- isDraft
1664
- headRefName
1665
- baseRefName
1666
- mergeable
1667
- headRefOid
1668
- reviewThreads(first: 100, after: $reviewThreadsAfter) {
1669
- pageInfo {
1670
- endCursor
1671
- hasNextPage
1672
- }
1673
- nodes {
1674
- isResolved
1675
- }
1676
- }
1677
- }
1678
- }
1679
- }
1680
- `;
1681
-
1682
- let slimPullRequest: SlimPullRequest | null = null;
1683
- let reviewThreadsAfter: string | null = null;
1684
- let hasNextPage = true;
1685
-
1686
- while (hasNextPage) {
1687
- const response = await this.fetchWithRateLimitRetry(() =>
1688
- fetchGithubGraphql({
1689
- ghToken: this.ghToken,
1690
- query,
1691
- variables: { owner, repo, prNumber, reviewThreadsAfter },
1692
- }),
1693
- );
1694
-
1695
- if (!response.ok) {
1696
- throw new Error(
1697
- `Failed to fetch pull request from GitHub GraphQL API: HTTP ${response.status}`,
1698
- );
1699
- }
1700
-
1701
- const responseData: unknown = await response.json();
1702
- if (!isSlimPullRequestResponse(responseData)) {
1703
- throw new Error('Unexpected response shape when fetching pull request');
1704
- }
1705
-
1706
- if (responseData.errors && responseData.errors.length > 0) {
1707
- throw new Error(
1708
- `GraphQL errors: ${JSON.stringify(responseData.errors)}`,
1709
- );
1710
- }
1711
-
1712
- const pr = responseData.data?.repository?.pullRequest;
1713
- if (!pr) {
1714
- return null;
1715
- }
1716
-
1717
- if (!slimPullRequest) {
1718
- slimPullRequest = {
1719
- url: pr.url,
1720
- state: pr.state,
1721
- isDraft: pr.isDraft,
1722
- headRefName: pr.headRefName,
1723
- baseRefName: pr.baseRefName,
1724
- mergeable: pr.mergeable,
1725
- headRefOid: pr.headRefOid,
1726
- reviewThreads: [],
1727
- };
1728
- }
1729
- for (const thread of pr.reviewThreads?.nodes || []) {
1730
- slimPullRequest.reviewThreads.push({ isResolved: thread.isResolved });
1731
- }
1732
-
1733
- hasNextPage = pr.reviewThreads?.pageInfo.hasNextPage === true;
1734
- reviewThreadsAfter = pr.reviewThreads?.pageInfo.endCursor ?? null;
1735
- }
1736
-
1737
- return slimPullRequest;
1738
- };
1739
-
1740
- private buildRelatedPullRequestFromSlim = async (
1741
- owner: string,
1742
- repo: string,
1743
- slimPullRequest: SlimPullRequest,
1744
- ): Promise<RelatedPullRequest> => {
1745
- const requiredCheckNames = slimPullRequest.baseRefName
1746
- ? await this.getRequiredCheckNames(
1747
- owner,
1748
- repo,
1749
- slimPullRequest.baseRefName,
1750
- )
1751
- : [];
1752
- const ciContexts = slimPullRequest.headRefOid
1753
- ? await this.getCommitCiContexts(owner, repo, slimPullRequest.headRefOid)
1754
- : [];
1755
- return this.computePrStatus(
1756
- slimPullRequest.url,
1757
- slimPullRequest.headRefName,
1758
- {
1759
- isDraft: slimPullRequest.isDraft,
1760
- mergeable: slimPullRequest.mergeable,
1761
- requiredCheckNames,
1762
- ciContexts,
1763
- reviewThreads: slimPullRequest.reviewThreads,
1764
- },
1765
- );
1766
- };
1767
-
1768
- private resolveMergeabilityWithRetry = async (
1769
- owner: string,
1770
- repo: string,
1771
- prNumber: number,
1772
- ): Promise<{
1773
- mergeable: string | null;
1774
- mergeStateStatus: string | null;
1775
- } | null> => {
1776
- const query = `
1777
- query PullRequestMergeability($owner: String!, $repo: String!, $prNumber: Int!) {
1778
- repository(owner: $owner, name: $repo) {
1779
- pullRequest(number: $prNumber) {
1780
- mergeable
1781
- mergeStateStatus
1782
- }
1783
- }
1784
- }
1785
- `;
1786
-
1787
- const maxAttempts = 3;
1788
- const retryDelayMilliseconds = 1000;
1789
- let lastResult: {
1790
- mergeable: string | null;
1791
- mergeStateStatus: string | null;
1792
- } | null = null;
1793
-
1794
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
1795
- if (attempt > 0) {
1796
- await this.sleep(retryDelayMilliseconds);
1797
- }
1798
-
1799
- const response = await this.fetchWithRateLimitRetry(() =>
1800
- fetchGithubGraphql({
1801
- ghToken: this.ghToken,
1802
- query,
1803
- variables: { owner, repo, prNumber },
1804
- }),
1805
- );
1806
-
1807
- if (!response.ok) {
1808
- throw new Error(
1809
- `Failed to fetch pull request mergeability from GitHub GraphQL API: HTTP ${response.status}`,
1810
- );
1811
- }
1812
-
1813
- const responseData: unknown = await response.json();
1814
- if (!isPullRequestMergeabilityResponse(responseData)) {
1815
- throw new Error(
1816
- `Unexpected response shape when fetching pull request mergeability: ${owner}/${repo}#${prNumber}`,
1817
- );
1818
- }
1819
-
1820
- if (responseData.errors && responseData.errors.length > 0) {
1821
- throw new Error(
1822
- `GraphQL errors: ${JSON.stringify(responseData.errors)}`,
1823
- );
1824
- }
1825
-
1826
- const pr = responseData.data?.repository?.pullRequest;
1827
- if (!pr) {
1828
- return null;
1829
- }
1830
-
1831
- lastResult = {
1832
- mergeable: pr.mergeable ?? null,
1833
- mergeStateStatus: pr.mergeStateStatus ?? null,
1834
- };
1835
-
1836
- if (lastResult.mergeable !== null && lastResult.mergeable !== 'UNKNOWN') {
1837
- return lastResult;
1838
- }
1839
- }
1840
-
1841
- return lastResult;
1842
- };
1843
-
1844
- findRelatedOpenPRs = async (
1845
- issueUrl: string,
1846
- ): Promise<RelatedPullRequest[]> => {
1847
- const { owner, repo, issueNumber, isPr } = this.parseIssueUrl(issueUrl);
1848
- if (isPr) {
1849
- throw new Error(
1850
- 'findRelatedOpenPRs only supports issue URLs, not pull request URLs',
1851
- );
1852
- }
1853
-
1854
- const query = `
1855
- query IssueRelatedOpenPullRequests($owner: String!, $repo: String!, $issueNumber: Int!, $after: String) {
1856
- repository(owner: $owner, name: $repo) {
1857
- issue(number: $issueNumber) {
1858
- timelineItems(first: 100, after: $after, itemTypes: [CROSS_REFERENCED_EVENT]) {
1859
- pageInfo {
1860
- endCursor
1861
- hasNextPage
1862
- }
1863
- nodes {
1864
- __typename
1865
- ... on CrossReferencedEvent {
1866
- willCloseTarget
1867
- source {
1868
- __typename
1869
- ... on PullRequest {
1870
- url
1871
- number
1872
- body
1873
- state
1874
- createdAt
1875
- isDraft
1876
- mergeable
1877
- headRefName
1878
- baseRefName
1879
- baseRef {
1880
- name
1881
- }
1882
- }
1883
- }
1884
- }
1885
- }
1886
- }
1887
- }
1888
- }
1889
- }
1890
- `;
1891
-
1892
- const relatedPRsMap: Map<string, RelatedPullRequest> = new Map();
1893
- let after: string | null = null;
1894
- let hasNextPage = true;
1895
-
1896
- while (hasNextPage) {
1897
- const response = await this.fetchWithRateLimitRetry(() =>
1898
- fetchGithubGraphql({
1899
- ghToken: this.ghToken,
1900
- query,
1901
- variables: { owner, repo, issueNumber, after },
1902
- }),
1903
- );
1904
-
1905
- if (!response.ok) {
1906
- await this.throwGitHubError(
1907
- 'Failed to fetch issue timeline from GitHub GraphQL API',
1908
- response,
1909
- );
1910
- }
1911
-
1912
- const responseData: unknown = await response.json();
1913
- if (!isIssueTimelineResponse(responseData)) {
1914
- throw new Error(
1915
- `Unexpected response shape when fetching issue timeline: ${issueUrl}`,
1916
- );
1917
- }
1918
-
1919
- const issueData = responseData.data?.repository?.issue;
1920
- if (!issueData) {
1921
- console.info(
1922
- `ApiV3CheerioRestIssueRepository: issue not found when fetching timeline, returning empty related PRs. issueUrl: ${issueUrl}`,
1923
- );
1924
- return [];
1925
- }
1926
-
1927
- for (const item of issueData.timelineItems.nodes) {
1928
- if (item.__typename !== 'CrossReferencedEvent') continue;
1929
- if (!item.source || item.source.__typename !== 'PullRequest') continue;
1930
- if (item.source.state !== 'OPEN') continue;
1931
- if (
1932
- !item.willCloseTarget &&
1933
- !this.prBodyContainsCrossRepoClosingKeyword(
1934
- item.source.body ?? null,
1935
- issueUrl,
1936
- )
1937
- )
1938
- continue;
1939
-
1940
- const pr = item.source;
1941
- const prUrl = pr.url || '';
1942
-
1943
- if (!prUrl) continue;
1944
-
1945
- const { owner: prOwner, repo: prRepo } = this.parseIssueUrl(prUrl);
1946
-
1947
- let isConflicted = pr.mergeable === 'CONFLICTING';
1948
- let mergeable: string | null = pr.mergeable ?? null;
1949
- if (
1950
- pr.number !== undefined &&
1951
- (pr.mergeable === undefined ||
1952
- pr.mergeable === null ||
1953
- pr.mergeable === 'UNKNOWN')
1954
- ) {
1955
- let resolved: {
1956
- mergeable: string | null;
1957
- mergeStateStatus: string | null;
1958
- } | null;
1959
- try {
1960
- resolved = await this.resolveMergeabilityWithRetry(
1961
- prOwner,
1962
- prRepo,
1963
- pr.number,
1964
- );
1965
- } catch (error) {
1966
- const errorMessage =
1967
- error instanceof Error ? error.message : String(error);
1968
- if (errorMessage.includes('NOT_FOUND')) {
1969
- console.info(
1970
- `ApiV3CheerioRestIssueRepository: pull request no longer exists, excluding it from related open PRs. prUrl: ${prUrl}`,
1971
- );
1972
- } else {
1973
- console.warn(
1974
- `ApiV3CheerioRestIssueRepository: resolveMergeabilityWithRetry failed, skipping PR for this cycle. prUrl: ${prUrl} error: ${errorMessage}`,
1975
- );
1976
- }
1977
- continue;
1978
- }
1979
- if (resolved !== null) {
1980
- mergeable = resolved.mergeable;
1981
- isConflicted =
1982
- resolved.mergeable === 'CONFLICTING' ||
1983
- resolved.mergeStateStatus === 'DIRTY';
1984
- }
1985
- }
1986
-
1987
- if (pr.number === undefined) continue;
1988
-
1989
- let prStatus: RelatedPullRequest;
1990
- try {
1991
- const slimPullRequest = await this.fetchSlimPullRequest(
1992
- prOwner,
1993
- prRepo,
1994
- pr.number,
1995
- );
1996
- if (!slimPullRequest || slimPullRequest.state !== 'OPEN') {
1997
- console.info(
1998
- `ApiV3CheerioRestIssueRepository: pull request is no longer open, excluding it from related open PRs. prUrl: ${prUrl}`,
1999
- );
2000
- continue;
2001
- }
2002
- const baseRefName =
2003
- slimPullRequest.baseRefName ?? pr.baseRefName ?? pr.baseRef?.name;
2004
- prStatus = await this.buildRelatedPullRequestFromSlim(
2005
- prOwner,
2006
- prRepo,
2007
- {
2008
- ...slimPullRequest,
2009
- url: slimPullRequest.url || prUrl,
2010
- headRefName: slimPullRequest.headRefName ?? pr.headRefName,
2011
- baseRefName,
2012
- },
2013
- );
2014
- } catch (error) {
2015
- const errorMessage =
2016
- error instanceof Error ? error.message : String(error);
2017
- console.warn(
2018
- `ApiV3CheerioRestIssueRepository: fetching pull request status failed, skipping PR for this cycle. prUrl: ${prUrl} error: ${errorMessage}`,
2019
- );
2020
- continue;
2021
- }
2022
-
2023
- relatedPRsMap.set(prUrl, {
2024
- ...prStatus,
2025
- isConflicted,
2026
- mergeable,
2027
- createdAt: pr.createdAt ? new Date(pr.createdAt) : new Date(0),
2028
- });
2029
- }
2030
-
2031
- hasNextPage = issueData.timelineItems.pageInfo.hasNextPage;
2032
- after = issueData.timelineItems.pageInfo.endCursor;
2033
- }
2034
-
2035
- return Array.from(relatedPRsMap.values());
2036
- };
2037
-
2038
- getAllOpened = async (project: Project): Promise<Issue[]> => {
2039
- const { issues } = await this.getAllIssues(project.id);
2040
- return issues.filter((issue) => !issue.isClosed);
2041
- };
2042
-
2043
- getStoryObjectMap = async (project: Project): Promise<StoryObjectMap> => {
2044
- const { issues } = await this.getAllIssues(project.id);
2045
- const storyObjectMap: StoryObjectMap = new Map();
2046
- const targetStories = project.story?.stories || [];
2047
- for (const story of targetStories) {
2048
- const storyIssue = issues.find((issue) =>
2049
- story.name.startsWith(issue.title),
2050
- );
2051
- storyObjectMap.set(story.name, {
2052
- story,
2053
- storyIssue: storyIssue || null,
2054
- issues: [],
2055
- });
2056
- for (const issue of issues) {
2057
- if (issue.story !== story.name) continue;
2058
- storyObjectMap.get(story.name)?.issues.push(issue);
2059
- }
2060
- }
2061
- return storyObjectMap;
2062
- };
2063
-
2064
- getOpenPullRequest = async (
2065
- prUrl: string,
2066
- ): Promise<RelatedPullRequest | null> => {
2067
- const parsedUrl = this.parseIssueUrl(prUrl);
2068
- if (!parsedUrl.isPr) {
2069
- return null;
2070
- }
2071
- const { owner, repo, issueNumber: prNumber } = parsedUrl;
2072
-
2073
- const slimPullRequest = await this.fetchSlimPullRequest(
2074
- owner,
2075
- repo,
2076
- prNumber,
2077
- );
2078
- if (!slimPullRequest || slimPullRequest.state !== 'OPEN') {
2079
- return null;
2080
- }
2081
-
2082
- return this.buildRelatedPullRequestFromSlim(owner, repo, slimPullRequest);
2083
- };
2084
-
2085
- private fetchRestPullRequestCiStatus = async (
2086
- owner: string,
2087
- repo: string,
2088
- prNumber: number,
2089
- prUrl: string,
2090
- ): Promise<RestPullRequestCiStatusResponse | null> => {
2091
- const maxAttempts = 3;
2092
- const retryDelayMilliseconds = 1000;
2093
- let lastPullRequest: RestPullRequestCiStatusResponse | null = null;
2094
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
2095
- if (attempt > 0) {
2096
- await this.sleep(retryDelayMilliseconds);
2097
- }
2098
- const response = await this.fetchWithRateLimitRetry(() =>
2099
- fetch(
2100
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}`,
2101
- {
2102
- method: 'GET',
2103
- headers: {
2104
- Authorization: `Bearer ${this.ghToken}`,
2105
- Accept: 'application/vnd.github+json',
2106
- },
2107
- },
2108
- ),
2109
- );
2110
- if (response.status === 404) {
2111
- return null;
2112
- }
2113
- if (!response.ok) {
2114
- await this.throwGitHubError(
2115
- `Failed to fetch pull request status for ${prUrl}`,
2116
- response,
2117
- );
2118
- }
2119
- const body: unknown = await response.json();
2120
- if (!isRestPullRequestCiStatusResponse(body)) {
2121
- throw new Error(
2122
- `Unexpected response shape when fetching pull request status for ${prUrl}`,
2123
- );
2124
- }
2125
- lastPullRequest = body;
2126
- if (body.state !== 'open' || body.mergeable !== null) {
2127
- return body;
2128
- }
2129
- }
2130
- return lastPullRequest;
2131
- };
2132
-
2133
- getOpenPullRequestCiStatus = async (
2134
- prUrl: string,
2135
- ): Promise<OpenPullRequestCiStatus | null> => {
2136
- const parsedUrl = this.parseIssueUrl(prUrl);
2137
- if (!parsedUrl.isPr) {
2138
- return null;
2139
- }
2140
- const { owner, repo, issueNumber: prNumber } = parsedUrl;
2141
-
2142
- const pullRequest = await this.fetchRestPullRequestCiStatus(
2143
- owner,
2144
- repo,
2145
- prNumber,
2146
- prUrl,
2147
- );
2148
- if (!pullRequest || pullRequest.state !== 'open') {
2149
- return null;
2150
- }
2151
-
2152
- const requiredCheckNames = await this.getRequiredCheckNames(
2153
- owner,
2154
- repo,
2155
- pullRequest.base.ref,
2156
- );
2157
- const ciContexts = await this.getCommitCiContexts(
2158
- owner,
2159
- repo,
2160
- pullRequest.head.sha,
2161
- );
2162
- const status = this.computePrStatus(
2163
- pullRequest.html_url,
2164
- pullRequest.head.ref,
2165
- {
2166
- isDraft: pullRequest.draft,
2167
- mergeable: graphqlMergeableFromRestMergeable(pullRequest.mergeable),
2168
- requiredCheckNames,
2169
- ciContexts,
2170
- reviewThreads: [],
2171
- },
2172
- );
2173
- return {
2174
- url: status.url,
2175
- isConflicted: status.isConflicted,
2176
- mergeable: status.mergeable,
2177
- isPassedAllCiJob: status.isPassedAllCiJob,
2178
- isCiStateSuccess: status.isCiStateSuccess,
2179
- isBranchOutOfDate: status.isBranchOutOfDate,
2180
- missingRequiredCheckNames: status.missingRequiredCheckNames,
2181
- };
2182
- };
2183
-
2184
- // Resolves many pull requests with one GraphQL query per hundred instead of
2185
- // one query per pull request. A URL this cannot settle is left out of the
2186
- // returned map rather than mapped to null, so the caller falls back to
2187
- // getOpenPullRequest for it and an unknown state is never mistaken for an
2188
- // absent pull request.
2189
- getOpenPullRequests = async (
2190
- prUrls: string[],
2191
- ): Promise<Map<string, RelatedPullRequest | null>> => {
2192
- const resolved = new Map<string, RelatedPullRequest | null>();
2193
- const parsedByUrl = new Map<
2194
- string,
2195
- { owner: string; repo: string; prNumber: number }
2196
- >();
2197
- for (const prUrl of Array.from(new Set(prUrls))) {
2198
- const parsedUrl = this.parseIssueUrl(prUrl);
2199
- if (!parsedUrl.isPr) {
2200
- resolved.set(prUrl, null);
2201
- continue;
2202
- }
2203
- parsedByUrl.set(prUrl, {
2204
- owner: parsedUrl.owner,
2205
- repo: parsedUrl.repo,
2206
- prNumber: parsedUrl.issueNumber,
2207
- });
2208
- }
2209
-
2210
- const urlsToFetch = Array.from(parsedByUrl.keys());
2211
- for (
2212
- let start = 0;
2213
- start < urlsToFetch.length;
2214
- start += SLIM_PULL_REQUEST_BATCH_SIZE
2215
- ) {
2216
- const batchUrls = urlsToFetch.slice(
2217
- start,
2218
- start + SLIM_PULL_REQUEST_BATCH_SIZE,
2219
- );
2220
- let slimByUrl: Map<string, SlimPullRequest | null>;
2221
- try {
2222
- slimByUrl = await this.fetchSlimPullRequestsInOneQuery(
2223
- batchUrls.map((prUrl) => ({
2224
- prUrl,
2225
- ...this.requireParsedPullRequest(parsedByUrl, prUrl),
2226
- })),
2227
- );
2228
- } catch (error) {
2229
- console.warn(
2230
- `ApiV3CheerioRestIssueRepository: batched pull request status query failed, leaving ${batchUrls.length} pull request(s) to per-request resolution. error: ${error instanceof Error ? error.message : String(error)}`,
2231
- );
2232
- continue;
2233
- }
2234
- for (const prUrl of batchUrls) {
2235
- if (!slimByUrl.has(prUrl)) {
2236
- continue;
2237
- }
2238
- const slimPullRequest = slimByUrl.get(prUrl) ?? null;
2239
- if (!slimPullRequest || slimPullRequest.state !== 'OPEN') {
2240
- resolved.set(prUrl, null);
2241
- continue;
2242
- }
2243
- const parsed = this.requireParsedPullRequest(parsedByUrl, prUrl);
2244
- try {
2245
- resolved.set(
2246
- prUrl,
2247
- await this.buildRelatedPullRequestFromSlim(
2248
- parsed.owner,
2249
- parsed.repo,
2250
- slimPullRequest,
2251
- ),
2252
- );
2253
- } catch (error) {
2254
- console.warn(
2255
- `ApiV3CheerioRestIssueRepository: building the pull request status failed, leaving it to per-request resolution. prUrl: ${prUrl} error: ${error instanceof Error ? error.message : String(error)}`,
2256
- );
2257
- }
2258
- }
2259
- }
2260
- return resolved;
2261
- };
2262
-
2263
- private requireParsedPullRequest = (
2264
- parsedByUrl: Map<string, { owner: string; repo: string; prNumber: number }>,
2265
- prUrl: string,
2266
- ): { owner: string; repo: string; prNumber: number } => {
2267
- const parsed = parsedByUrl.get(prUrl);
2268
- if (!parsed) {
2269
- throw new Error(`Pull request URL was not parsed: ${prUrl}`);
2270
- }
2271
- return parsed;
2272
- };
2273
-
2274
- // A pull request whose review threads do not fit the first page is omitted
2275
- // from the returned map, so the caller resolves it through the paginating
2276
- // single-pull-request path and the resolved-thread state stays complete.
2277
- private fetchSlimPullRequestsInOneQuery = async (
2278
- references: {
2279
- prUrl: string;
2280
- owner: string;
2281
- repo: string;
2282
- prNumber: number;
2283
- }[],
2284
- ): Promise<Map<string, SlimPullRequest | null>> => {
2285
- const aliasOf = (index: number): string => `pullRequest${index}`;
2286
- const variableDeclarations = references
2287
- .map(
2288
- (_, index) =>
2289
- `$owner${index}: String!, $repo${index}: String!, $prNumber${index}: Int!`,
2290
- )
2291
- .join(', ');
2292
- const selections = references
2293
- .map(
2294
- (
2295
- _,
2296
- index,
2297
- ) => ` ${aliasOf(index)}: repository(owner: $owner${index}, name: $repo${index}) {
2298
- pullRequest(number: $prNumber${index}) {
2299
- url
2300
- state
2301
- isDraft
2302
- headRefName
2303
- baseRefName
2304
- mergeable
2305
- headRefOid
2306
- reviewThreads(first: ${SLIM_PULL_REQUEST_REVIEW_THREADS_PAGE_SIZE}) {
2307
- pageInfo {
2308
- endCursor
2309
- hasNextPage
2310
- }
2311
- nodes {
2312
- isResolved
2313
- }
2314
- }
2315
- }
2316
- }`,
2317
- )
2318
- .join('\n');
2319
- const query = `query PullRequestSlimStatusBatch(${variableDeclarations}) {\n${selections}\n}`;
2320
- const variables: Record<string, string | number> = {};
2321
- references.forEach((reference, index) => {
2322
- variables[`owner${index}`] = reference.owner;
2323
- variables[`repo${index}`] = reference.repo;
2324
- variables[`prNumber${index}`] = reference.prNumber;
2325
- });
2326
-
2327
- const response = await this.fetchWithRateLimitRetry(() =>
2328
- fetchGithubGraphql({ ghToken: this.ghToken, query, variables }),
2329
- );
2330
- if (!response.ok) {
2331
- throw new Error(
2332
- `Failed to fetch pull requests from GitHub GraphQL API: HTTP ${response.status}`,
2333
- );
2334
- }
2335
- const responseData: unknown = await response.json();
2336
- if (!isSlimPullRequestBatchResponse(responseData)) {
2337
- throw new Error('Unexpected response shape when fetching pull requests');
2338
- }
2339
- const errors = responseData.errors || [];
2340
- const notFoundAliases = new Set(
2341
- errors
2342
- .filter((error) => error.type === 'NOT_FOUND')
2343
- .map((error) => String(error.path?.[0] ?? '')),
2344
- );
2345
- const otherErrorAliases = new Set(
2346
- errors
2347
- .filter((error) => error.type !== 'NOT_FOUND')
2348
- .map((error) => String(error.path?.[0] ?? '')),
2349
- );
2350
- if (!responseData.data) {
2351
- throw new Error(`GraphQL errors: ${JSON.stringify(errors)}`);
2352
- }
2353
- const unattributedError = errors.some(
2354
- (error) => error.type !== 'NOT_FOUND' && !error.path,
2355
- );
2356
- if (unattributedError) {
2357
- throw new Error(`GraphQL errors: ${JSON.stringify(errors)}`);
2358
- }
2359
-
2360
- const slimByUrl = new Map<string, SlimPullRequest | null>();
2361
- references.forEach((reference, index) => {
2362
- const alias = aliasOf(index);
2363
- if (otherErrorAliases.has(alias)) {
2364
- return;
2365
- }
2366
- const node = responseData.data?.[alias]?.pullRequest ?? null;
2367
- if (!node) {
2368
- if (notFoundAliases.has(alias) || !errors.length) {
2369
- slimByUrl.set(reference.prUrl, null);
2370
- }
2371
- return;
2372
- }
2373
- if (node.reviewThreads?.pageInfo.hasNextPage === true) {
2374
- return;
2375
- }
2376
- slimByUrl.set(reference.prUrl, {
2377
- url: node.url,
2378
- state: node.state,
2379
- isDraft: node.isDraft,
2380
- headRefName: node.headRefName,
2381
- baseRefName: node.baseRefName,
2382
- mergeable: node.mergeable,
2383
- headRefOid: node.headRefOid,
2384
- reviewThreads: (node.reviewThreads?.nodes || []).map((thread) => ({
2385
- isResolved: thread.isResolved,
2386
- })),
2387
- });
2388
- });
2389
- return slimByUrl;
2390
- };
2391
-
2392
- closePullRequest = async (prUrl: string): Promise<void> => {
2393
- const { owner, repo, issueNumber: prNumber } = this.parseIssueUrl(prUrl);
2394
- const response = await this.fetchWithRateLimitRetry(() =>
2395
- fetch(
2396
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}`,
2397
- {
2398
- method: 'PATCH',
2399
- headers: {
2400
- Authorization: `Bearer ${this.ghToken}`,
2401
- 'Content-Type': 'application/json',
2402
- },
2403
- body: JSON.stringify({ state: 'closed' }),
2404
- },
2405
- ),
2406
- );
2407
- if (!response.ok) {
2408
- const reason = await this.formatGitHubErrorWithStatus(response);
2409
- throw new Error(`Failed to close PR ${prUrl}: ${reason}`);
2410
- }
2411
- };
2412
-
2413
- closeIssueByUrl = async (
2414
- issueUrl: string,
2415
- stateReason: 'completed' | 'not_planned',
2416
- ): Promise<void> => {
2417
- const { owner, repo, issueNumber } = this.parseIssueUrl(issueUrl);
2418
- const ownerSegment = encodeURIComponent(owner);
2419
- const repoSegment = encodeURIComponent(repo);
2420
- const response = await this.fetchWithRateLimitRetry(() =>
2421
- fetch(
2422
- `https://api.github.com/repos/${ownerSegment}/${repoSegment}/issues/${issueNumber}`,
2423
- {
2424
- method: 'PATCH',
2425
- headers: {
2426
- Authorization: `Bearer ${this.ghToken}`,
2427
- 'Content-Type': 'application/json',
2428
- },
2429
- body: JSON.stringify({ state: 'closed', state_reason: stateReason }),
2430
- },
2431
- ),
2432
- );
2433
- if (!response.ok) {
2434
- const reason = await this.formatGitHubErrorWithStatus(response);
2435
- throw new Error(`Failed to close issue ${issueUrl}: ${reason}`);
2436
- }
2437
- };
2438
-
2439
- getPullRequestChangedFilePaths = async (prUrl: string): Promise<string[]> => {
2440
- const { owner, repo, issueNumber: prNumber } = this.parseIssueUrl(prUrl);
2441
- const perPage = 100;
2442
- const collectedPaths: string[] = [];
2443
- let page = 1;
2444
- let hasMore = true;
2445
- while (hasMore) {
2446
- const response = await this.fetchWithRateLimitRetry(() =>
2447
- fetch(
2448
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/files?per_page=${perPage}&page=${page}`,
2449
- {
2450
- method: 'GET',
2451
- headers: {
2452
- Authorization: `Bearer ${this.ghToken}`,
2453
- Accept: 'application/vnd.github+json',
2454
- },
2455
- },
2456
- ),
2457
- );
2458
- if (!response.ok) {
2459
- const reason = await this.formatGitHubErrorWithStatus(response);
2460
- throw new Error(
2461
- `Failed to fetch changed files for PR ${prUrl}: ${reason}`,
2462
- );
2463
- }
2464
- const body: unknown = await response.json();
2465
- if (!isPullRequestFilesResponse(body)) {
2466
- throw new Error(
2467
- `Unexpected response shape when fetching changed files for PR ${prUrl}`,
2468
- );
2469
- }
2470
- for (const file of body) {
2471
- collectedPaths.push(file.filename);
2472
- }
2473
- if (body.length < perPage) {
2474
- hasMore = false;
2475
- } else {
2476
- page += 1;
2477
- }
2478
- }
2479
- return collectedPaths;
2480
- };
2481
-
2482
- getAuthenticatedUserLogin = async (): Promise<string> => {
2483
- const response = await this.fetchWithRateLimitRetry(() =>
2484
- fetch('https://api.github.com/user', {
2485
- method: 'GET',
2486
- headers: {
2487
- Authorization: `Bearer ${this.ghToken}`,
2488
- Accept: 'application/vnd.github+json',
2489
- },
2490
- }),
2491
- );
2492
- if (!response.ok) {
2493
- const reason = await this.formatGitHubErrorWithStatus(response);
2494
- throw new Error(`Failed to fetch authenticated user: ${reason}`);
2495
- }
2496
- const body: unknown = await response.json();
2497
- if (!isAuthenticatedUserResponse(body)) {
2498
- throw new Error(
2499
- 'Unexpected response shape when fetching authenticated user',
2500
- );
2501
- }
2502
- return body.login;
2503
- };
2504
-
2505
- approvePullRequest = async (prUrl: string): Promise<void> => {
2506
- const { owner, repo, issueNumber: prNumber } = this.parseIssueUrl(prUrl);
2507
- const response = await this.fetchWithRateLimitRetry(() =>
2508
- fetch(
2509
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/reviews`,
2510
- {
2511
- method: 'POST',
2512
- headers: {
2513
- Authorization: `Bearer ${this.ghToken}`,
2514
- 'Content-Type': 'application/json',
2515
- Accept: 'application/vnd.github+json',
2516
- },
2517
- body: JSON.stringify({ event: 'APPROVE' }),
2518
- },
2519
- ),
2520
- );
2521
- if (!response.ok) {
2522
- const reason = await this.formatGitHubErrorWithStatus(response);
2523
- throw new Error(`Failed to approve PR ${prUrl}: ${reason}`);
2524
- }
2525
- };
2526
-
2527
- mergePullRequest = async (prUrl: string): Promise<void> => {
2528
- const { owner, repo, issueNumber: prNumber } = this.parseIssueUrl(prUrl);
2529
- const mergeUrl = `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/merge`;
2530
- const headers = {
2531
- Authorization: `Bearer ${this.ghToken}`,
2532
- 'Content-Type': 'application/json',
2533
- Accept: 'application/vnd.github+json',
2534
- };
2535
- const mergeWith = (mergeMethod?: 'squash' | 'rebase') =>
2536
- this.fetchWithRateLimitRetry(() =>
2537
- fetch(mergeUrl, {
2538
- method: 'PUT',
2539
- headers,
2540
- body: JSON.stringify(
2541
- mergeMethod ? { merge_method: mergeMethod } : {},
2542
- ),
2543
- }),
2544
- );
2545
- const response = await mergeWith();
2546
- if (response.ok) {
2547
- return;
2548
- }
2549
- if (response.status === 405) {
2550
- const fallbackMethod = await this.resolveAllowedMergeMethod(owner, repo);
2551
- if (fallbackMethod !== null) {
2552
- const retryResponse = await mergeWith(fallbackMethod);
2553
- if (retryResponse.ok) {
2554
- return;
2555
- }
2556
- const retryReason =
2557
- await this.formatGitHubErrorWithStatus(retryResponse);
2558
- throw new Error(`Failed to merge PR ${prUrl}: ${retryReason}`);
2559
- }
2560
- }
2561
- const reason = await this.formatGitHubErrorWithStatus(response);
2562
- throw new Error(`Failed to merge PR ${prUrl}: ${reason}`);
2563
- };
2564
-
2565
- private resolveAllowedMergeMethod = async (
2566
- owner: string,
2567
- repo: string,
2568
- ): Promise<'squash' | 'rebase' | null> => {
2569
- const response = await this.fetchWithRateLimitRetry(() =>
2570
- fetch(
2571
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`,
2572
- {
2573
- headers: {
2574
- Authorization: `Bearer ${this.ghToken}`,
2575
- Accept: 'application/vnd.github+json',
2576
- },
2577
- },
2578
- ),
2579
- );
2580
- if (!response.ok) {
2581
- return null;
2582
- }
2583
- const data: unknown = await response.json();
2584
- if (!isRepoMergeSettings(data)) {
2585
- return null;
2586
- }
2587
- if (data.allow_squash_merge === true) {
2588
- return 'squash';
2589
- }
2590
- if (data.allow_rebase_merge === true) {
2591
- return 'rebase';
2592
- }
2593
- return null;
2594
- };
2595
-
2596
- requestChangesWithInlineComment = async (
2597
- prUrl: string,
2598
- changedFilePath: string | null,
2599
- commentBody: string,
2600
- inlineCommentLocation: PullRequestReviewInlineLocation | null = null,
2601
- ): Promise<void> => {
2602
- const { owner, repo, issueNumber: prNumber } = this.parseIssueUrl(prUrl);
2603
- if (changedFilePath === null) {
2604
- await this.createCommentByUrl(prUrl, commentBody);
2605
- return;
2606
- }
2607
- const inlineComment =
2608
- inlineCommentLocation === null
2609
- ? { path: changedFilePath, position: 1, body: commentBody }
2610
- : {
2611
- path: changedFilePath,
2612
- line: inlineCommentLocation.line,
2613
- side: inlineCommentLocation.side,
2614
- body: commentBody,
2615
- };
2616
- const reviewBody: Record<string, unknown> = {
2617
- event: 'REQUEST_CHANGES',
2618
- body: commentBody,
2619
- comments: [inlineComment],
2620
- };
2621
- const response = await this.fetchWithRateLimitRetry(() =>
2622
- fetch(
2623
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/reviews`,
2624
- {
2625
- method: 'POST',
2626
- headers: {
2627
- Authorization: `Bearer ${this.ghToken}`,
2628
- 'Content-Type': 'application/json',
2629
- Accept: 'application/vnd.github+json',
2630
- },
2631
- body: JSON.stringify(reviewBody),
2632
- },
2633
- ),
2634
- );
2635
- if (!response.ok) {
2636
- const reason = await this.formatGitHubErrorWithStatus(response);
2637
- if (
2638
- response.status === 422 &&
2639
- reason.includes(SELF_AUTHORED_REVIEW_REFUSAL)
2640
- ) {
2641
- await this.recordRequestedChangesWithoutReview(
2642
- prUrl,
2643
- changedFilePath,
2644
- commentBody,
2645
- inlineCommentLocation,
2646
- );
2647
- return;
2648
- }
2649
- throw new Error(`Failed to request changes on PR ${prUrl}: ${reason}`);
2650
- }
2651
- };
2652
-
2653
- private recordRequestedChangesWithoutReview = async (
2654
- prUrl: string,
2655
- changedFilePath: string,
2656
- commentBody: string,
2657
- inlineCommentLocation: PullRequestReviewInlineLocation | null,
2658
- ): Promise<void> => {
2659
- if (inlineCommentLocation === null) {
2660
- await this.createCommentByUrl(prUrl, commentBody);
2661
- return;
2662
- }
2663
- await this.createPullRequestReviewComment(
2664
- prUrl,
2665
- changedFilePath,
2666
- inlineCommentLocation.line,
2667
- inlineCommentLocation.side,
2668
- commentBody,
2669
- );
2670
- };
2671
-
2672
- private fetchPullRequestHeadSha = async (
2673
- owner: string,
2674
- repo: string,
2675
- prNumber: number,
2676
- prUrl: string,
2677
- ): Promise<string> => {
2678
- const ownerSegment = encodeURIComponent(owner);
2679
- const repoSegment = encodeURIComponent(repo);
2680
- const response = await this.fetchWithRateLimitRetry(() =>
2681
- fetch(
2682
- `https://api.github.com/repos/${ownerSegment}/${repoSegment}/pulls/${prNumber}`,
2683
- {
2684
- method: 'GET',
2685
- headers: {
2686
- Authorization: `Bearer ${this.ghToken}`,
2687
- Accept: 'application/vnd.github+json',
2688
- },
2689
- },
2690
- ),
2691
- );
2692
- if (!response.ok) {
2693
- const reason = await this.formatGitHubErrorWithStatus(response);
2694
- throw new Error(`Failed to fetch head commit for PR ${prUrl}: ${reason}`);
2695
- }
2696
- const body: unknown = await response.json();
2697
- if (
2698
- !isRecord(body) ||
2699
- !isRecord(body.head) ||
2700
- typeof body.head.sha !== 'string'
2701
- ) {
2702
- throw new Error(
2703
- `Unexpected response shape when fetching head commit for PR ${prUrl}`,
2704
- );
2705
- }
2706
- return body.head.sha;
2707
- };
2708
-
2709
- createPullRequestReviewComment = async (
2710
- prUrl: string,
2711
- path: string,
2712
- line: number,
2713
- side: PullRequestReviewCommentSide,
2714
- commentBody: string,
2715
- ): Promise<void> => {
2716
- const { owner, repo, issueNumber: prNumber } = this.parseIssueUrl(prUrl);
2717
- const commitId = await this.fetchPullRequestHeadSha(
2718
- owner,
2719
- repo,
2720
- prNumber,
2721
- prUrl,
2722
- );
2723
- const ownerSegment = encodeURIComponent(owner);
2724
- const repoSegment = encodeURIComponent(repo);
2725
- const response = await this.fetchWithRateLimitRetry(() =>
2726
- fetch(
2727
- `https://api.github.com/repos/${ownerSegment}/${repoSegment}/pulls/${prNumber}/comments`,
2728
- {
2729
- method: 'POST',
2730
- headers: {
2731
- Authorization: `Bearer ${this.ghToken}`,
2732
- 'Content-Type': 'application/json',
2733
- Accept: 'application/vnd.github+json',
2734
- },
2735
- body: JSON.stringify({
2736
- body: commentBody,
2737
- commit_id: commitId,
2738
- path,
2739
- line,
2740
- side,
2741
- }),
2742
- },
2743
- ),
2744
- );
2745
- if (!response.ok) {
2746
- const reason = await this.formatGitHubErrorWithStatus(response);
2747
- throw new Error(
2748
- `Failed to create review comment on PR ${prUrl}: ${reason}`,
2749
- );
2750
- }
2751
- };
2752
-
2753
- private readGitHubErrorReason = async (
2754
- response: Response,
2755
- ): Promise<string | null> => {
2756
- let parsed: unknown;
2757
- try {
2758
- parsed = await response.json();
2759
- } catch {
2760
- return null;
2761
- }
2762
- if (!isRecord(parsed) || typeof parsed.message !== 'string') {
2763
- return null;
2764
- }
2765
- if (Array.isArray(parsed.errors) && parsed.errors.length > 0) {
2766
- const details = parsed.errors
2767
- .map((error) => {
2768
- if (typeof error === 'string') {
2769
- return error;
2770
- }
2771
- if (isRecord(error) && typeof error.message === 'string') {
2772
- return error.message;
2773
- }
2774
- return '';
2775
- })
2776
- .filter((detail) => detail.length > 0)
2777
- .join('; ');
2778
- if (details.length > 0) {
2779
- return `${parsed.message}: ${details}`;
2780
- }
2781
- }
2782
- return parsed.message;
2783
- };
2784
-
2785
- private formatGitHubErrorWithStatus = async (
2786
- response: Response,
2787
- ): Promise<string> => {
2788
- const status = `HTTP ${response.status}`;
2789
- const bodyText = await response.clone().text();
2790
- const reason = await this.readGitHubErrorReason(response);
2791
- if (hasRateLimitSignals(response.status, response.headers, bodyText)) {
2792
- const resetIso = computeRateLimitResetIso(response.headers);
2793
- const resetSuffix = resetIso === null ? '' : ` (resets at ${resetIso})`;
2794
- return `${status} GitHub rate limit exceeded, please retry shortly${resetSuffix}`;
2795
- }
2796
- if (response.status === 403) {
2797
- const permissionSuffix = reason === null ? '' : ` ${reason}`;
2798
- return `${status} permission denied, the token cannot perform this operation${permissionSuffix}`;
2799
- }
2800
- if (reason === null) {
2801
- return status;
2802
- }
2803
- return `${status} ${reason}`;
2804
- };
2805
-
2806
- updateBranch = async (prUrl: string): Promise<boolean> => {
2807
- const { owner, repo, issueNumber: prNumber } = this.parseIssueUrl(prUrl);
2808
- const response = await this.fetchWithRateLimitRetry(() =>
2809
- fetch(
2810
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/update-branch`,
2811
- {
2812
- method: 'PUT',
2813
- headers: {
2814
- Authorization: `Bearer ${this.ghToken}`,
2815
- Accept: 'application/vnd.github+json',
2816
- },
2817
- },
2818
- ),
2819
- );
2820
- if (response.ok) {
2821
- return true;
2822
- }
2823
- if (response.status === 422) {
2824
- return false;
2825
- }
2826
- const reason = await this.formatGitHubErrorWithStatus(response);
2827
- throw new Error(`Failed to update branch for PR ${prUrl}: ${reason}`);
2828
- };
2829
-
2830
- deletePullRequestBranch = async (
2831
- prUrl: string,
2832
- branchName: string,
2833
- ): Promise<void> => {
2834
- const { owner, repo } = this.parseIssueUrl(prUrl);
2835
- const response = await this.fetchWithRateLimitRetry(() =>
2836
- fetch(
2837
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/git/refs/heads/${encodeURIComponent(branchName)}`,
2838
- {
2839
- method: 'DELETE',
2840
- headers: {
2841
- Authorization: `Bearer ${this.ghToken}`,
2842
- },
2843
- },
2844
- ),
2845
- );
2846
- if (!response.ok && response.status !== 422) {
2847
- const reason = await this.formatGitHubErrorWithStatus(response);
2848
- throw new Error(
2849
- `Failed to delete branch ${branchName} for PR ${prUrl}: ${reason}`,
2850
- );
2851
- }
2852
- };
2853
-
2854
- createCommentByUrl = async (
2855
- issueOrPrUrl: string,
2856
- commentBody: string,
2857
- ): Promise<void> => {
2858
- await this.restIssueRepository.createComment(issueOrPrUrl, commentBody);
2859
- };
2860
-
2861
- private fetchIssueBodyResponse = (url: string): Promise<Response> => {
2862
- const { owner, repo, issueNumber } = this.parseIssueUrl(url);
2863
- return this.fetchWithRateLimitRetry(() =>
2864
- fetch(
2865
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}`,
2866
- {
2867
- method: 'GET',
2868
- headers: {
2869
- Authorization: `Bearer ${this.ghToken}`,
2870
- Accept: 'application/vnd.github+json',
2871
- },
2872
- },
2873
- ),
2874
- );
2875
- };
2876
-
2877
- private parseIssueBodyResponse = async (
2878
- url: string,
2879
- response: Response,
2880
- ): Promise<string> => {
2881
- const body: unknown = await response.json();
2882
- if (!isIssueOrPullRequestBodyResponse(body)) {
2883
- throw new Error(
2884
- `Unexpected response shape when fetching body for ${url}`,
2885
- );
2886
- }
2887
- return body.body ?? '';
2888
- };
2889
-
2890
- getIssueOrPullRequestBody = async (url: string): Promise<string> => {
2891
- const response = await this.fetchIssueBodyResponse(url);
2892
- if (!response.ok) {
2893
- await this.throwGitHubError(`Failed to fetch body for ${url}`, response);
2894
- }
2895
- return this.parseIssueBodyResponse(url, response);
2896
- };
2897
-
2898
- getIssueBodyByUrl = async (url: string): Promise<string | null> => {
2899
- const response = await this.fetchIssueBodyResponse(url);
2900
- if (response.status === 404) {
2901
- return null;
2902
- }
2903
- if (!response.ok) {
2904
- const reason = await this.formatGitHubErrorWithStatus(response);
2905
- throw new Error(`Failed to fetch body for ${url}: ${reason}`);
2906
- }
2907
- return this.parseIssueBodyResponse(url, response);
2908
- };
2909
-
2910
- getIssueOrPullRequestComments = async (
2911
- url: string,
2912
- ): Promise<IssueComment[]> => {
2913
- const { owner, repo, issueNumber } = this.parseIssueUrl(url);
2914
- const perPage = 100;
2915
- const collectedComments: IssueComment[] = [];
2916
- let page = 1;
2917
- let hasMore = true;
2918
- while (hasMore) {
2919
- const response = await this.fetchWithRateLimitRetry(() =>
2920
- fetch(
2921
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}/comments?per_page=${perPage}&page=${page}`,
2922
- {
2923
- method: 'GET',
2924
- headers: {
2925
- Authorization: `Bearer ${this.ghToken}`,
2926
- Accept: 'application/vnd.github+json',
2927
- },
2928
- },
2929
- ),
2930
- );
2931
- if (!response.ok) {
2932
- await this.throwGitHubError(
2933
- `Failed to fetch comments for ${url}`,
2934
- response,
2935
- );
2936
- }
2937
- const body: unknown = await response.json();
2938
- if (!isIssueCommentsResponse(body)) {
2939
- throw new Error(
2940
- `Unexpected response shape when fetching comments for ${url}`,
2941
- );
2942
- }
2943
- for (const comment of body) {
2944
- collectedComments.push({
2945
- author: comment.user?.login ?? '',
2946
- body: comment.body ?? '',
2947
- createdAt: new Date(comment.created_at),
2948
- });
2949
- }
2950
- if (body.length < perPage) {
2951
- hasMore = false;
2952
- } else {
2953
- page += 1;
2954
- }
2955
- }
2956
- return collectedComments;
2957
- };
2958
-
2959
- getPullRequestDetail = async (
2960
- prUrl: string,
2961
- ): Promise<PullRequestDetail | null> => {
2962
- const {
2963
- owner,
2964
- repo,
2965
- issueNumber: prNumber,
2966
- isPr,
2967
- } = this.parseIssueUrl(prUrl);
2968
- if (!isPr) {
2969
- return null;
2970
- }
2971
- const detailResponse = await this.fetchWithRateLimitRetry(() =>
2972
- fetch(
2973
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}`,
2974
- {
2975
- method: 'GET',
2976
- headers: {
2977
- Authorization: `Bearer ${this.ghToken}`,
2978
- Accept: 'application/vnd.github+json',
2979
- },
2980
- },
2981
- ),
2982
- );
2983
- if (!detailResponse.ok) {
2984
- await this.throwGitHubError(
2985
- `Failed to fetch detail for PR ${prUrl}`,
2986
- detailResponse,
2987
- );
2988
- }
2989
- const detailBody: unknown = await detailResponse.json();
2990
- if (!isPullRequestDetailResponse(detailBody)) {
2991
- throw new Error(
2992
- `Unexpected response shape when fetching detail for PR ${prUrl}`,
2993
- );
2994
- }
2995
- const files = await this.fetchPullRequestFiles(
2996
- owner,
2997
- repo,
2998
- prNumber,
2999
- prUrl,
3000
- );
3001
- return {
3002
- title: detailBody.title,
3003
- state: detailBody.state,
3004
- merged: detailBody.merged,
3005
- isDraft: detailBody.draft,
3006
- additions: detailBody.additions,
3007
- deletions: detailBody.deletions,
3008
- changedFiles: detailBody.changed_files,
3009
- headRefName: detailBody.head.ref,
3010
- baseRefName: detailBody.base.ref,
3011
- author: detailBody.user?.login ?? '',
3012
- files,
3013
- };
3014
- };
3015
-
3016
- private fetchPullRequestFiles = async (
3017
- owner: string,
3018
- repo: string,
3019
- prNumber: number,
3020
- prUrl: string,
3021
- ): Promise<PullRequestFile[]> => {
3022
- const perPage = 100;
3023
- const collectedFiles: PullRequestFile[] = [];
3024
- let page = 1;
3025
- let hasMore = true;
3026
- while (hasMore) {
3027
- const response = await this.fetchWithRateLimitRetry(() =>
3028
- fetch(
3029
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/files?per_page=${perPage}&page=${page}`,
3030
- {
3031
- method: 'GET',
3032
- headers: {
3033
- Authorization: `Bearer ${this.ghToken}`,
3034
- Accept: 'application/vnd.github+json',
3035
- },
3036
- },
3037
- ),
3038
- );
3039
- if (!response.ok) {
3040
- await this.throwGitHubError(
3041
- `Failed to fetch files for PR ${prUrl}`,
3042
- response,
3043
- );
3044
- }
3045
- const body: unknown = await response.json();
3046
- if (!isPullRequestDetailFilesResponse(body)) {
3047
- throw new Error(
3048
- `Unexpected response shape when fetching files for PR ${prUrl}`,
3049
- );
3050
- }
3051
- for (const file of body) {
3052
- collectedFiles.push({
3053
- filename: file.filename,
3054
- status: file.status,
3055
- additions: file.additions,
3056
- deletions: file.deletions,
3057
- patch: file.patch ?? null,
3058
- rawUrl:
3059
- file.raw_url === undefined
3060
- ? null
3061
- : normalizeGitHubRawUrl(file.raw_url),
3062
- });
3063
- }
3064
- if (body.length < perPage) {
3065
- hasMore = false;
3066
- } else {
3067
- page += 1;
3068
- }
3069
- }
3070
- return collectedFiles;
3071
- };
3072
-
3073
- getPullRequestCommits = async (
3074
- prUrl: string,
3075
- ): Promise<PullRequestCommit[]> => {
3076
- const {
3077
- owner,
3078
- repo,
3079
- issueNumber: prNumber,
3080
- isPr,
3081
- } = this.parseIssueUrl(prUrl);
3082
- if (!isPr) {
3083
- return [];
3084
- }
3085
- const perPage = 100;
3086
- const collectedCommits: PullRequestCommit[] = [];
3087
- let page = 1;
3088
- let hasMore = true;
3089
- while (hasMore) {
3090
- const response = await this.fetchWithRateLimitRetry(() =>
3091
- fetch(
3092
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}/commits?per_page=${perPage}&page=${page}`,
3093
- {
3094
- method: 'GET',
3095
- headers: {
3096
- Authorization: `Bearer ${this.ghToken}`,
3097
- Accept: 'application/vnd.github+json',
3098
- },
3099
- },
3100
- ),
3101
- );
3102
- if (!response.ok) {
3103
- await this.throwGitHubError(
3104
- `Failed to fetch commits for PR ${prUrl}`,
3105
- response,
3106
- );
3107
- }
3108
- const body: unknown = await response.json();
3109
- if (!isPullRequestCommitsResponse(body)) {
3110
- throw new Error(
3111
- `Unexpected response shape when fetching commits for PR ${prUrl}`,
3112
- );
3113
- }
3114
- for (const commit of body) {
3115
- collectedCommits.push({
3116
- sha: commit.sha,
3117
- message: commit.commit.message,
3118
- author: commit.commit.author?.name ?? '',
3119
- authoredAt: new Date(commit.commit.author?.date ?? 0),
3120
- });
3121
- }
3122
- if (body.length < perPage) {
3123
- hasMore = false;
3124
- } else {
3125
- page += 1;
3126
- }
3127
- }
3128
- return collectedCommits;
3129
- };
3130
-
3131
- getIssueOrPullRequestState = async (
3132
- url: string,
3133
- ): Promise<{
3134
- state: string;
3135
- merged: boolean;
3136
- isPullRequest: boolean;
3137
- title: string;
3138
- }> => {
3139
- const { owner, repo, issueNumber, isPr } = this.parseIssueUrl(url);
3140
- if (isPr) {
3141
- const response = await this.fetchWithRateLimitRetry(() =>
3142
- fetch(
3143
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${issueNumber}`,
3144
- {
3145
- method: 'GET',
3146
- headers: {
3147
- Authorization: `Bearer ${this.ghToken}`,
3148
- Accept: 'application/vnd.github+json',
3149
- },
3150
- },
3151
- ),
3152
- );
3153
- if (!response.ok) {
3154
- await this.throwGitHubError(
3155
- `Failed to fetch state for ${url}`,
3156
- response,
3157
- );
3158
- }
3159
- const body: unknown = await response.json();
3160
- if (!isPullRequestDetailResponse(body)) {
3161
- throw new Error(
3162
- `Unexpected response shape when fetching state for ${url}`,
3163
- );
3164
- }
3165
- return {
3166
- state: body.state,
3167
- merged: body.merged,
3168
- isPullRequest: true,
3169
- title: body.title,
3170
- };
3171
- }
3172
- const response = await this.fetchWithRateLimitRetry(() =>
3173
- fetch(
3174
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}`,
3175
- {
3176
- method: 'GET',
3177
- headers: {
3178
- Authorization: `Bearer ${this.ghToken}`,
3179
- Accept: 'application/vnd.github+json',
3180
- },
3181
- },
3182
- ),
3183
- );
3184
- if (!response.ok) {
3185
- await this.throwGitHubError(`Failed to fetch state for ${url}`, response);
3186
- }
3187
- const body: unknown = await response.json();
3188
- if (!isIssueOrPullRequestStateResponse(body)) {
3189
- throw new Error(
3190
- `Unexpected response shape when fetching state for ${url}`,
3191
- );
3192
- }
3193
- return {
3194
- state: body.state,
3195
- merged: false,
3196
- isPullRequest: false,
3197
- title: body.title,
3198
- };
3199
- };
3200
-
3201
- getPullRequestSummary = async (
3202
- prUrl: string,
3203
- ): Promise<{
3204
- title: string;
3205
- body: string;
3206
- additions: number;
3207
- deletions: number;
3208
- changedFiles: number;
3209
- } | null> => {
3210
- const {
3211
- owner,
3212
- repo,
3213
- issueNumber: prNumber,
3214
- isPr,
3215
- } = this.parseIssueUrl(prUrl);
3216
- if (!isPr) {
3217
- return null;
3218
- }
3219
- const response = await this.fetchWithRateLimitRetry(() =>
3220
- fetch(
3221
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/pulls/${prNumber}`,
3222
- {
3223
- method: 'GET',
3224
- headers: {
3225
- Authorization: `Bearer ${this.ghToken}`,
3226
- Accept: 'application/vnd.github+json',
3227
- },
3228
- },
3229
- ),
3230
- );
3231
- if (!response.ok) {
3232
- const reason = await this.formatGitHubErrorWithStatus(response);
3233
- throw new Error(`Failed to fetch summary for PR ${prUrl}: ${reason}`);
3234
- }
3235
- const body: unknown = await response.json();
3236
- if (!isPullRequestDetailResponse(body)) {
3237
- throw new Error(
3238
- `Unexpected response shape when fetching summary for PR ${prUrl}`,
3239
- );
3240
- }
3241
- return {
3242
- title: body.title,
3243
- body: body.body ?? '',
3244
- additions: body.additions,
3245
- deletions: body.deletions,
3246
- changedFiles: body.changed_files,
3247
- };
3248
- };
3249
-
3250
- deleteAllCommentsByUrl = async (issueOrPrUrl: string): Promise<void> => {
3251
- const { owner, repo, issueNumber } = this.parseIssueUrl(issueOrPrUrl);
3252
- const perPage = 100;
3253
- while (true) {
3254
- const response = await this.fetchWithRateLimitRetry(() =>
3255
- fetch(
3256
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/${issueNumber}/comments?per_page=${perPage}&page=1`,
3257
- {
3258
- method: 'GET',
3259
- headers: {
3260
- Authorization: `Bearer ${this.ghToken}`,
3261
- Accept: 'application/vnd.github+json',
3262
- },
3263
- },
3264
- ),
3265
- );
3266
- if (!response.ok) {
3267
- await this.throwGitHubError(
3268
- `Failed to fetch comments for ${issueOrPrUrl}`,
3269
- response,
3270
- );
3271
- }
3272
- const body: unknown = await response.json();
3273
- if (!isIssueCommentIdResponse(body)) {
3274
- throw new Error(
3275
- `Unexpected response shape when fetching comments for ${issueOrPrUrl}`,
3276
- );
3277
- }
3278
- for (const comment of body) {
3279
- const deleteResponse = await this.fetchWithRateLimitRetry(() =>
3280
- fetch(
3281
- `https://api.github.com/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/issues/comments/${comment.id}`,
3282
- {
3283
- method: 'DELETE',
3284
- headers: {
3285
- Authorization: `Bearer ${this.ghToken}`,
3286
- Accept: 'application/vnd.github+json',
3287
- },
3288
- },
3289
- ),
3290
- );
3291
- if (!deleteResponse.ok) {
3292
- await this.throwGitHubError(
3293
- `Failed to delete comment ${comment.id} on ${issueOrPrUrl}`,
3294
- deleteResponse,
3295
- );
3296
- }
3297
- }
3298
- if (body.length < perPage) {
3299
- break;
3300
- }
3301
- }
3302
- };
3303
- }