github-issue-tower-defence-management 2.19.0 → 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 (783) hide show
  1. package/README.md +1 -1
  2. package/bin/adapter/entry-points/cli/index.js +0 -0
  3. package/bin/domain/usecases/OauthTokenSelectUseCase.js +1 -1
  4. package/package.json +5 -1
  5. package/types/domain/usecases/OauthTokenSelectUseCase.d.ts +1 -1
  6. package/.env.example +0 -7
  7. package/.gitattributes +0 -2
  8. package/.github/CODEOWNERS +0 -6
  9. package/.github/copilot-instructions.md +0 -6
  10. package/.github/dependabot.yml +0 -19
  11. package/.github/instructions/code-review.instructions.md +0 -50
  12. package/.github/workflows/api-created_issue_pr.yml +0 -97
  13. package/.github/workflows/ci-failure-issue-creator.yml +0 -48
  14. package/.github/workflows/close-manual-prs.yml +0 -80
  15. package/.github/workflows/codeql.yml +0 -107
  16. package/.github/workflows/commit-lint.yml +0 -85
  17. package/.github/workflows/configs/commitlint.config.cjs +0 -32
  18. package/.github/workflows/configs/commitlint.config.js +0 -32
  19. package/.github/workflows/console-ui.yml +0 -68
  20. package/.github/workflows/create-pr.yml +0 -116
  21. package/.github/workflows/empty-format-test-job.yml +0 -34
  22. package/.github/workflows/format.yml +0 -28
  23. package/.github/workflows/publish.yml +0 -111
  24. package/.github/workflows/secret-scan.yml +0 -51
  25. package/.github/workflows/test.yml +0 -58
  26. package/.github/workflows/umino-project.yml +0 -207
  27. package/.gitleaks.toml +0 -22
  28. package/.prettierignore +0 -31
  29. package/.prettierrc +0 -5
  30. package/CHANGELOG.md +0 -4155
  31. package/CONTRIBUTING.md +0 -60
  32. package/bin/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.js +0 -12
  33. package/bin/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.js.map +0 -1
  34. package/bin/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.js +0 -125
  35. package/bin/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.js.map +0 -1
  36. package/bin/adapter/repositories/issue/issueTimelineUtils.js +0 -38
  37. package/bin/adapter/repositories/issue/issueTimelineUtils.js.map +0 -1
  38. package/bin/domain/entities/UnansweredOwnerCall.js +0 -3
  39. package/bin/domain/entities/UnansweredOwnerCall.js.map +0 -1
  40. package/bin/domain/entities/WorkingTime.js +0 -3
  41. package/bin/domain/entities/WorkingTime.js.map +0 -1
  42. package/bin/domain/usecases/ChangeStatusLongInReviewIssueUseCase.js +0 -37
  43. package/bin/domain/usecases/ChangeStatusLongInReviewIssueUseCase.js.map +0 -1
  44. package/bin/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.js +0 -118
  45. package/bin/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.js.map +0 -1
  46. package/bin/domain/usecases/GenerateWorkingTimeReportUseCase.js +0 -180
  47. package/bin/domain/usecases/GenerateWorkingTimeReportUseCase.js.map +0 -1
  48. package/bin/domain/usecases/TriagerApprovalDispatchUseCase.js +0 -160
  49. package/bin/domain/usecases/TriagerApprovalDispatchUseCase.js.map +0 -1
  50. package/bin/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.js +0 -3
  51. package/bin/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.js.map +0 -1
  52. package/bin/domain/usecases/awaitingOwnerApprovalMessage.js +0 -7
  53. package/bin/domain/usecases/awaitingOwnerApprovalMessage.js.map +0 -1
  54. package/bin/domain/usecases/findLastAgentDeclaringReport.js +0 -12
  55. package/bin/domain/usecases/findLastAgentDeclaringReport.js.map +0 -1
  56. package/bin/domain/usecases/isPullRequestDeclaredUnnecessary.js +0 -25
  57. package/bin/domain/usecases/isPullRequestDeclaredUnnecessary.js.map +0 -1
  58. package/bin/domain/usecases/isWaitingForOwnerApproval.js +0 -16
  59. package/bin/domain/usecases/isWaitingForOwnerApproval.js.map +0 -1
  60. package/bin/domain/usecases/returnedToAwaitingWorkspaceMessage.js +0 -7
  61. package/bin/domain/usecases/returnedToAwaitingWorkspaceMessage.js.map +0 -1
  62. package/commitlint.config.js +0 -6
  63. package/eslint.config.mjs +0 -76
  64. package/jest.config.js +0 -84
  65. package/jest.setup.js +0 -1
  66. package/lib/buffer-equal-constant-time-compat/index.js +0 -17
  67. package/lib/buffer-equal-constant-time-compat/index.test.js +0 -54
  68. package/lib/buffer-equal-constant-time-compat/package.json +0 -5
  69. package/lib/typescript-compat/bin/tsc +0 -2
  70. package/lib/typescript-compat/bin/tsserver +0 -2
  71. package/lib/typescript-compat/lib/typescript.js +0 -1
  72. package/lib/typescript-compat/package.json +0 -13
  73. package/renovate.json +0 -46
  74. package/scripts/copyConsoleUiDist.mjs +0 -35
  75. package/scripts/defaultBranchTipVerify.sh +0 -23
  76. package/scripts/testWorkflowRunVerify.sh +0 -47
  77. package/scripts/verifyConsoleUiDist.mjs +0 -96
  78. package/src/adapter/ci/consoleUiWorkflowTimeout.test.ts +0 -61
  79. package/src/adapter/ci/publishTestWorkflowGate.test.ts +0 -653
  80. package/src/adapter/ci/workflowRunCancellation.test.ts +0 -72
  81. package/src/adapter/entry-points/cli/fleetConfig.test.ts +0 -553
  82. package/src/adapter/entry-points/cli/fleetConfig.ts +0 -266
  83. package/src/adapter/entry-points/cli/index.test.ts +0 -2837
  84. package/src/adapter/entry-points/cli/index.ts +0 -1488
  85. package/src/adapter/entry-points/cli/projectConfig.test.ts +0 -342
  86. package/src/adapter/entry-points/cli/projectConfig.ts +0 -500
  87. package/src/adapter/entry-points/console/consoleAirplaneSnapshotApi.test.ts +0 -658
  88. package/src/adapter/entry-points/console/consoleAirplaneSnapshotApi.ts +0 -482
  89. package/src/adapter/entry-points/console/consoleDataDelivery.test.ts +0 -222
  90. package/src/adapter/entry-points/console/consoleDataDelivery.ts +0 -134
  91. package/src/adapter/entry-points/console/consoleDoneStore.test.ts +0 -169
  92. package/src/adapter/entry-points/console/consoleDoneStore.ts +0 -139
  93. package/src/adapter/entry-points/console/consoleGithubTokenResolver.test.ts +0 -275
  94. package/src/adapter/entry-points/console/consoleGithubTokenResolver.ts +0 -113
  95. package/src/adapter/entry-points/console/consoleImageProxy.test.ts +0 -140
  96. package/src/adapter/entry-points/console/consoleImageProxy.ts +0 -111
  97. package/src/adapter/entry-points/console/consoleItemUrlLookup.test.ts +0 -63
  98. package/src/adapter/entry-points/console/consoleItemUrlLookup.ts +0 -66
  99. package/src/adapter/entry-points/console/consoleOperationApi.test.ts +0 -2523
  100. package/src/adapter/entry-points/console/consoleOperationApi.ts +0 -936
  101. package/src/adapter/entry-points/console/consoleProjectResolver.test.ts +0 -246
  102. package/src/adapter/entry-points/console/consoleProjectResolver.ts +0 -111
  103. package/src/adapter/entry-points/console/consoleReadApi.test.ts +0 -714
  104. package/src/adapter/entry-points/console/consoleReadApi.ts +0 -361
  105. package/src/adapter/entry-points/console/consoleTabNames.test.ts +0 -52
  106. package/src/adapter/entry-points/console/consoleTabNames.ts +0 -37
  107. package/src/adapter/entry-points/console/consoleUiDistSync.test.ts +0 -66
  108. package/src/adapter/entry-points/console/dashboardComposeService.test.ts +0 -595
  109. package/src/adapter/entry-points/console/dashboardComposeService.ts +0 -256
  110. package/src/adapter/entry-points/console/dashboardEmitterComposerKey.test.ts +0 -180
  111. package/src/adapter/entry-points/console/ensureConsoleRunning.test.ts +0 -115
  112. package/src/adapter/entry-points/console/ensureConsoleRunning.ts +0 -64
  113. package/src/adapter/entry-points/console/ui/.storybook/main.ts +0 -12
  114. package/src/adapter/entry-points/console/ui/.storybook/preview.ts +0 -15
  115. package/src/adapter/entry-points/console/ui/biome.json +0 -53
  116. package/src/adapter/entry-points/console/ui/components.json +0 -20
  117. package/src/adapter/entry-points/console/ui/e2e/consoleScenario.spec.ts +0 -725
  118. package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +0 -831
  119. package/src/adapter/entry-points/console/ui/e2e/playwright.config.ts +0 -30
  120. package/src/adapter/entry-points/console/ui/e2e/tsconfig.json +0 -18
  121. package/src/adapter/entry-points/console/ui/index.html +0 -12
  122. package/src/adapter/entry-points/console/ui/jest-globals.cjs +0 -26
  123. package/src/adapter/entry-points/console/ui/jest.setup.ts +0 -1
  124. package/src/adapter/entry-points/console/ui/jest.styleMock.js +0 -1
  125. package/src/adapter/entry-points/console/ui/public/sw.js +0 -60
  126. package/src/adapter/entry-points/console/ui/src/components/ui/badge.stories.tsx +0 -35
  127. package/src/adapter/entry-points/console/ui/src/components/ui/badge.tsx +0 -28
  128. package/src/adapter/entry-points/console/ui/src/components/ui/button.stories.tsx +0 -34
  129. package/src/adapter/entry-points/console/ui/src/components/ui/button.tsx +0 -50
  130. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleCopyCodeButton.stories.tsx +0 -28
  131. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleCopyCodeButton.test.tsx +0 -65
  132. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleCopyCodeButton.tsx +0 -19
  133. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.stories.tsx +0 -61
  134. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.test.tsx +0 -199
  135. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMarkdownContent.tsx +0 -166
  136. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.stories.tsx +0 -22
  137. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.test.tsx +0 -74
  138. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagram.tsx +0 -84
  139. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagramModalScreen.stories.tsx +0 -27
  140. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagramModalScreen.test.tsx +0 -85
  141. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleMermaidDiagramModalScreen.tsx +0 -97
  142. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.stories.tsx +0 -76
  143. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.test.tsx +0 -56
  144. package/src/adapter/entry-points/console/ui/src/features/console/components/content/ConsoleReferenceLink.tsx +0 -39
  145. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.stories.tsx +0 -32
  146. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.test.tsx +0 -105
  147. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleChangedFileList.tsx +0 -157
  148. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.stories.tsx +0 -68
  149. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.test.tsx +0 -612
  150. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentComposer.tsx +0 -276
  151. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.stories.tsx +0 -33
  152. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.test.tsx +0 -57
  153. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommentList.tsx +0 -76
  154. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.stories.tsx +0 -25
  155. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.test.tsx +0 -55
  156. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCommitList.tsx +0 -49
  157. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCopyUrlButton.stories.tsx +0 -24
  158. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCopyUrlButton.test.tsx +0 -63
  159. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleCopyUrlButton.tsx +0 -19
  160. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFetchFailureAlert.stories.tsx +0 -33
  161. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFetchFailureAlert.test.tsx +0 -62
  162. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFetchFailureAlert.tsx +0 -59
  163. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.stories.tsx +0 -38
  164. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.test.tsx +0 -151
  165. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleFileDiff.tsx +0 -245
  166. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.stories.tsx +0 -514
  167. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.test.tsx +0 -727
  168. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.tsx +0 -387
  169. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.stories.tsx +0 -82
  170. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.test.tsx +0 -52
  171. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemIcon.tsx +0 -32
  172. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.stories.tsx +0 -48
  173. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.test.tsx +0 -235
  174. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.tsx +0 -145
  175. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestMergeableChip.stories.tsx +0 -29
  176. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestMergeableChip.test.tsx +0 -30
  177. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestMergeableChip.tsx +0 -50
  178. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.stories.tsx +0 -38
  179. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.test.tsx +0 -54
  180. package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.tsx +0 -60
  181. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.stories.tsx +0 -56
  182. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.test.tsx +0 -111
  183. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleAirplaneModeButton.tsx +0 -92
  184. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.stories.tsx +0 -51
  185. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.test.tsx +0 -59
  186. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsolePanel.tsx +0 -39
  187. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.stories.tsx +0 -207
  188. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.test.tsx +0 -332
  189. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTabList.tsx +0 -211
  190. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTimerSettingsModalDialog.stories.tsx +0 -49
  191. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTimerSettingsModalDialog.test.tsx +0 -157
  192. package/src/adapter/entry-points/console/ui/src/features/console/components/layout/ConsoleTimerSettingsModalDialog.tsx +0 -118
  193. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleCopyStoryNameButton.stories.tsx +0 -23
  194. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleCopyStoryNameButton.test.tsx +0 -40
  195. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleCopyStoryNameButton.tsx +0 -17
  196. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.stories.tsx +0 -101
  197. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.test.tsx +0 -102
  198. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemList.tsx +0 -75
  199. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.stories.tsx +0 -40
  200. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.test.tsx +0 -213
  201. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleItemSummary.tsx +0 -115
  202. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedItemRow.stories.tsx +0 -50
  203. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedItemRow.test.tsx +0 -98
  204. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedItemRow.tsx +0 -66
  205. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedList.stories.tsx +0 -69
  206. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedList.test.tsx +0 -101
  207. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleQueuedList.tsx +0 -75
  208. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.stories.tsx +0 -137
  209. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.test.tsx +0 -554
  210. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryList.tsx +0 -400
  211. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryReorderPanel.stories.tsx +0 -30
  212. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryReorderPanel.test.tsx +0 -111
  213. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStoryReorderPanel.tsx +0 -91
  214. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.stories.tsx +0 -27
  215. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.test.tsx +0 -24
  216. package/src/adapter/entry-points/console/ui/src/features/console/components/list/ConsoleStorySummary.tsx +0 -28
  217. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.stories.tsx +0 -14
  218. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.test.tsx +0 -21
  219. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleCloseActions.tsx +0 -26
  220. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.stories.tsx +0 -14
  221. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.test.tsx +0 -40
  222. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleDangerousActions.tsx +0 -37
  223. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleErrorToast.stories.tsx +0 -33
  224. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.stories.tsx +0 -20
  225. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.test.tsx +0 -48
  226. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleNextActionDateActions.tsx +0 -42
  227. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOfflinePendingActionsPanel.stories.tsx +0 -115
  228. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOfflinePendingActionsPanel.test.tsx +0 -262
  229. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOfflinePendingActionsPanel.tsx +0 -123
  230. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.stories.tsx +0 -65
  231. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.test.tsx +0 -159
  232. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleOperationMenu.tsx +0 -55
  233. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.stories.tsx +0 -20
  234. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.test.tsx +0 -93
  235. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsolePullRequestReviewActions.tsx +0 -45
  236. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.stories.tsx +0 -17
  237. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.test.tsx +0 -49
  238. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleStatusActions.tsx +0 -66
  239. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.stories.tsx +0 -47
  240. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.test.tsx +0 -95
  241. package/src/adapter/entry-points/console/ui/src/features/console/components/operations/ConsoleUndoToast.tsx +0 -62
  242. package/src/adapter/entry-points/console/ui/src/features/console/components/shared/ConsoleClipboardCopyButton.stories.tsx +0 -32
  243. package/src/adapter/entry-points/console/ui/src/features/console/components/shared/ConsoleClipboardCopyButton.test.tsx +0 -101
  244. package/src/adapter/entry-points/console/ui/src/features/console/components/shared/ConsoleClipboardCopyButton.tsx +0 -80
  245. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useAirplaneMode.test.ts +0 -304
  246. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useAirplaneMode.ts +0 -167
  247. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleActionQueue.test.ts +0 -498
  248. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleActionQueue.ts +0 -277
  249. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.test.ts +0 -99
  250. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleCaches.ts +0 -90
  251. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleDetailPrefetch.test.ts +0 -110
  252. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleDetailPrefetch.ts +0 -83
  253. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleFeaturesConfig.test.ts +0 -51
  254. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleFeaturesConfig.ts +0 -39
  255. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.test.ts +0 -229
  256. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.ts +0 -194
  257. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleNavigation.test.ts +0 -151
  258. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleNavigation.ts +0 -128
  259. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.test.ts +0 -629
  260. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts +0 -417
  261. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOverlay.test.ts +0 -40
  262. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOverlay.ts +0 -71
  263. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsolePjcode.test.ts +0 -24
  264. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsolePjcode.ts +0 -17
  265. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectList.test.ts +0 -97
  266. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectList.ts +0 -44
  267. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectTimer.test.ts +0 -59
  268. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleProjectTimer.ts +0 -26
  269. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleReferenceLink.test.ts +0 -48
  270. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleReferenceLink.ts +0 -39
  271. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleResource.test.ts +0 -41
  272. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleResource.ts +0 -57
  273. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleSwipeNavigation.test.ts +0 -115
  274. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleSwipeNavigation.ts +0 -115
  275. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.test.ts +0 -546
  276. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTabData.ts +0 -278
  277. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTimerSettings.test.ts +0 -110
  278. package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleTimerSettings.ts +0 -70
  279. package/src/adapter/entry-points/console/ui/src/features/console/lib/airplaneSnapshot.test.ts +0 -387
  280. package/src/adapter/entry-points/console/ui/src/features/console/lib/airplaneSnapshot.ts +0 -407
  281. package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.test.ts +0 -446
  282. package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.ts +0 -533
  283. package/src/adapter/entry-points/console/ui/src/features/console/lib/imageProxy.test.ts +0 -78
  284. package/src/adapter/entry-points/console/ui/src/features/console/lib/imageProxy.ts +0 -34
  285. package/src/adapter/entry-points/console/ui/src/features/console/lib/markdown.test.ts +0 -252
  286. package/src/adapter/entry-points/console/ui/src/features/console/lib/markdown.ts +0 -207
  287. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidLoader.test.ts +0 -27
  288. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidLoader.ts +0 -71
  289. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidZoom.test.ts +0 -88
  290. package/src/adapter/entry-points/console/ui/src/features/console/lib/mermaidZoom.ts +0 -34
  291. package/src/adapter/entry-points/console/ui/src/features/console/lib/navigation.ts +0 -7
  292. package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.test.ts +0 -104
  293. package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.ts +0 -79
  294. package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.test.ts +0 -200
  295. package/src/adapter/entry-points/console/ui/src/features/console/logic/actionToast.ts +0 -122
  296. package/src/adapter/entry-points/console/ui/src/features/console/logic/clipboard.test.ts +0 -90
  297. package/src/adapter/entry-points/console/ui/src/features/console/logic/clipboard.ts +0 -37
  298. package/src/adapter/entry-points/console/ui/src/features/console/logic/colors.test.ts +0 -34
  299. package/src/adapter/entry-points/console/ui/src/features/console/logic/colors.ts +0 -85
  300. package/src/adapter/entry-points/console/ui/src/features/console/logic/diff.test.ts +0 -81
  301. package/src/adapter/entry-points/console/ui/src/features/console/logic/diff.ts +0 -61
  302. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileStatus.test.ts +0 -35
  303. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileStatus.ts +0 -21
  304. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileTree.test.ts +0 -96
  305. package/src/adapter/entry-points/console/ui/src/features/console/logic/fileTree.ts +0 -93
  306. package/src/adapter/entry-points/console/ui/src/features/console/logic/grouping.test.ts +0 -229
  307. package/src/adapter/entry-points/console/ui/src/features/console/logic/grouping.ts +0 -106
  308. package/src/adapter/entry-points/console/ui/src/features/console/logic/imageFile.test.ts +0 -18
  309. package/src/adapter/entry-points/console/ui/src/features/console/logic/imageFile.ts +0 -16
  310. package/src/adapter/entry-points/console/ui/src/features/console/logic/itemIcons.test.ts +0 -97
  311. package/src/adapter/entry-points/console/ui/src/features/console/logic/itemIcons.ts +0 -95
  312. package/src/adapter/entry-points/console/ui/src/features/console/logic/navigation.test.ts +0 -53
  313. package/src/adapter/entry-points/console/ui/src/features/console/logic/navigation.ts +0 -33
  314. package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.test.ts +0 -72
  315. package/src/adapter/entry-points/console/ui/src/features/console/logic/operations.ts +0 -61
  316. package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.test.ts +0 -325
  317. package/src/adapter/entry-points/console/ui/src/features/console/logic/overlay.ts +0 -94
  318. package/src/adapter/entry-points/console/ui/src/features/console/logic/postedComments.test.ts +0 -44
  319. package/src/adapter/entry-points/console/ui/src/features/console/logic/postedComments.ts +0 -17
  320. package/src/adapter/entry-points/console/ui/src/features/console/logic/references.test.ts +0 -103
  321. package/src/adapter/entry-points/console/ui/src/features/console/logic/references.ts +0 -64
  322. package/src/adapter/entry-points/console/ui/src/features/console/logic/relativeTime.test.ts +0 -74
  323. package/src/adapter/entry-points/console/ui/src/features/console/logic/relativeTime.ts +0 -53
  324. package/src/adapter/entry-points/console/ui/src/features/console/logic/swipe.test.ts +0 -34
  325. package/src/adapter/entry-points/console/ui/src/features/console/logic/swipe.ts +0 -28
  326. package/src/adapter/entry-points/console/ui/src/features/console/logic/tabAdvance.test.ts +0 -89
  327. package/src/adapter/entry-points/console/ui/src/features/console/logic/tabAdvance.ts +0 -30
  328. package/src/adapter/entry-points/console/ui/src/features/console/logic/timerSettings.test.ts +0 -61
  329. package/src/adapter/entry-points/console/ui/src/features/console/logic/timerSettings.ts +0 -77
  330. package/src/adapter/entry-points/console/ui/src/features/console/logic/types.ts +0 -173
  331. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +0 -629
  332. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.tsx +0 -351
  333. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.test.tsx +0 -1469
  334. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +0 -736
  335. package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleReferenceLinkContainer.tsx +0 -25
  336. package/src/adapter/entry-points/console/ui/src/features/console/testing/fixtures.ts +0 -418
  337. package/src/adapter/entry-points/console/ui/src/index.css +0 -1592
  338. package/src/adapter/entry-points/console/ui/src/lib/utils.ts +0 -4
  339. package/src/adapter/entry-points/console/ui/src/main.tsx +0 -21
  340. package/src/adapter/entry-points/console/ui/src/vite-env.d.ts +0 -1
  341. package/src/adapter/entry-points/console/ui/tsconfig.json +0 -26
  342. package/src/adapter/entry-points/console/ui/vite.config.ts +0 -24
  343. package/src/adapter/entry-points/console/ui-dist/assets/index-BYDbK1Fj.js +0 -88
  344. package/src/adapter/entry-points/console/ui-dist/assets/index-KyQISRuQ.css +0 -2
  345. package/src/adapter/entry-points/console/ui-dist/index.html +0 -13
  346. package/src/adapter/entry-points/console/ui-dist/sw.js +0 -60
  347. package/src/adapter/entry-points/console/webServer.test.ts +0 -2945
  348. package/src/adapter/entry-points/console/webServer.ts +0 -940
  349. package/src/adapter/entry-points/function/getStoryObjectMap.ts +0 -3
  350. package/src/adapter/entry-points/handlers/FileSystemConsoleTabsRepository.test.ts +0 -218
  351. package/src/adapter/entry-points/handlers/FileSystemConsoleTabsRepository.ts +0 -113
  352. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.test.ts +0 -110
  353. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.ts +0 -116
  354. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.test.ts +0 -859
  355. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +0 -984
  356. package/src/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.test.ts +0 -126
  357. package/src/adapter/entry-points/handlers/InTmuxByHumanSessionTokenCountHandler.ts +0 -80
  358. package/src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.test.ts +0 -491
  359. package/src/adapter/entry-points/handlers/LiveSessionOauthTokenSelectHandler.ts +0 -137
  360. package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.test.ts +0 -508
  361. package/src/adapter/entry-points/handlers/OauthTokenSelectHandler.ts +0 -156
  362. package/src/adapter/entry-points/handlers/consoleListsWriter.test.ts +0 -332
  363. package/src/adapter/entry-points/handlers/consoleListsWriter.ts +0 -60
  364. package/src/adapter/entry-points/handlers/dashboardRowWriter.test.ts +0 -112
  365. package/src/adapter/entry-points/handlers/dashboardRowWriter.ts +0 -51
  366. package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.test.ts +0 -285
  367. package/src/adapter/entry-points/handlers/inTmuxByHumanDataWriter.ts +0 -109
  368. package/src/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.test.ts +0 -170
  369. package/src/adapter/entry-points/handlers/inTmuxByHumanSessionReconciler.ts +0 -40
  370. package/src/adapter/entry-points/handlers/machineStatusWriter.test.ts +0 -191
  371. package/src/adapter/entry-points/handlers/machineStatusWriter.ts +0 -110
  372. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.test.ts +0 -548
  373. package/src/adapter/entry-points/handlers/notifySilentTmuxSessions.ts +0 -192
  374. package/src/adapter/entry-points/handlers/ownerCallFileCleaner.test.ts +0 -111
  375. package/src/adapter/entry-points/handlers/ownerCallFileCleaner.ts +0 -25
  376. package/src/adapter/entry-points/handlers/ownerCallFileStore.test.ts +0 -286
  377. package/src/adapter/entry-points/handlers/ownerCallFileStore.ts +0 -145
  378. package/src/adapter/entry-points/handlers/ownerReplyMarkerDirectoryResolve.test.ts +0 -55
  379. package/src/adapter/entry-points/handlers/ownerReplyMarkerDirectoryResolve.ts +0 -36
  380. package/src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.test.ts +0 -72
  381. package/src/adapter/entry-points/handlers/resetDegeneratedTmuxSessions.ts +0 -74
  382. package/src/adapter/entry-points/handlers/rotationOrderFileWriter.test.ts +0 -181
  383. package/src/adapter/entry-points/handlers/rotationOrderFileWriter.ts +0 -18
  384. package/src/adapter/entry-points/handlers/situationFileWriter.test.ts +0 -489
  385. package/src/adapter/entry-points/handlers/situationFileWriter.ts +0 -176
  386. package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.test.ts +0 -169
  387. package/src/adapter/entry-points/handlers/staleTmuxSessionCleaner.ts +0 -32
  388. package/src/adapter/entry-points/handlers/subscriptionDisabledTokenHandler.ts +0 -41
  389. package/src/adapter/entry-points/handlers/tokenExhaustionHandover.ts +0 -81
  390. package/src/adapter/entry-points/handlers/tokenStatusWriter.test.ts +0 -266
  391. package/src/adapter/entry-points/handlers/tokenStatusWriter.ts +0 -262
  392. package/src/adapter/proxy/ClaudeMessageResponseParser.test.ts +0 -211
  393. package/src/adapter/proxy/ClaudeMessageResponseParser.ts +0 -181
  394. package/src/adapter/proxy/RateLimitCache.test.ts +0 -1077
  395. package/src/adapter/proxy/RateLimitCache.ts +0 -385
  396. package/src/adapter/proxy/TokenListLoader.test.ts +0 -183
  397. package/src/adapter/proxy/TokenListLoader.ts +0 -75
  398. package/src/adapter/proxy/ensureProxyRunning.test.ts +0 -85
  399. package/src/adapter/proxy/ensureProxyRunning.ts +0 -41
  400. package/src/adapter/proxy/proxyEntry.test.ts +0 -552
  401. package/src/adapter/proxy/proxyEntry.ts +0 -214
  402. package/src/adapter/repositories/BaseGitHubRepository.test.ts +0 -26
  403. package/src/adapter/repositories/BaseGitHubRepository.ts +0 -26
  404. package/src/adapter/repositories/BrowserGitHubProjectRepository.test.ts +0 -108
  405. package/src/adapter/repositories/BrowserGitHubProjectRepository.ts +0 -118
  406. package/src/adapter/repositories/CliGitHubGraphqlRateLimitRepository.test.ts +0 -71
  407. package/src/adapter/repositories/CliGitHubGraphqlRateLimitRepository.ts +0 -21
  408. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.test.ts +0 -324
  409. package/src/adapter/repositories/ConfigurableSilentSessionMessageComposer.ts +0 -148
  410. package/src/adapter/repositories/FetchWebhookRepository.ts +0 -8
  411. package/src/adapter/repositories/FileHandoverStateRepository.test.ts +0 -114
  412. package/src/adapter/repositories/FileHandoverStateRepository.ts +0 -73
  413. package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.test.ts +0 -514
  414. package/src/adapter/repositories/FileSystemInteractiveLiveSessionTranscriptResolver.ts +0 -109
  415. package/src/adapter/repositories/FileSystemKevReportWatermarkRepository.test.ts +0 -284
  416. package/src/adapter/repositories/FileSystemKevReportWatermarkRepository.ts +0 -128
  417. package/src/adapter/repositories/FileSystemSessionAssistantTurnsRepository.test.ts +0 -138
  418. package/src/adapter/repositories/FileSystemSessionAssistantTurnsRepository.ts +0 -113
  419. package/src/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.test.ts +0 -121
  420. package/src/adapter/repositories/FileSystemSessionDegenerationCooldownStateRepository.ts +0 -103
  421. package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.test.ts +0 -360
  422. package/src/adapter/repositories/FileSystemSessionOutputActivityRepository.ts +0 -77
  423. package/src/adapter/repositories/FileSystemSessionRecordReader.test.ts +0 -59
  424. package/src/adapter/repositories/FileSystemSessionRecordReader.ts +0 -31
  425. package/src/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.test.ts +0 -287
  426. package/src/adapter/repositories/FileSystemSilentSessionCandidateStateRepository.ts +0 -125
  427. package/src/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.test.ts +0 -285
  428. package/src/adapter/repositories/FileSystemSilentSessionHubTaskStatusCacheRepository.ts +0 -119
  429. package/src/adapter/repositories/FileSystemSubAgentLivenessResolver.test.ts +0 -237
  430. package/src/adapter/repositories/FileSystemSubAgentLivenessResolver.ts +0 -57
  431. package/src/adapter/repositories/FileSystemSubAgentSilentSecondsResolver.test.ts +0 -56
  432. package/src/adapter/repositories/FileSystemSubAgentSilentSecondsResolver.ts +0 -32
  433. package/src/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.test.ts +0 -68
  434. package/src/adapter/repositories/FileSystemSubAgentTranscriptDirectoryResolver.ts +0 -23
  435. package/src/adapter/repositories/GitHubIssueCheckpointRepository.test.ts +0 -51
  436. package/src/adapter/repositories/GitHubIssueCheckpointRepository.ts +0 -47
  437. package/src/adapter/repositories/GitHubIssueCommentRepository.test.ts +0 -278
  438. package/src/adapter/repositories/GitHubIssueCommentRepository.ts +0 -109
  439. package/src/adapter/repositories/GoogleSpreadsheetRepository.integration.test.ts +0 -265
  440. package/src/adapter/repositories/GoogleSpreadsheetRepository.test.ts +0 -431
  441. package/src/adapter/repositories/GoogleSpreadsheetRepository.ts +0 -268
  442. package/src/adapter/repositories/GraphqlProjectRepository.diskCache.test.ts +0 -272
  443. package/src/adapter/repositories/GraphqlProjectRepository.errorHandling.test.ts +0 -111
  444. package/src/adapter/repositories/GraphqlProjectRepository.fetchProjectId.test.ts +0 -254
  445. package/src/adapter/repositories/GraphqlProjectRepository.test.ts +0 -209
  446. package/src/adapter/repositories/GraphqlProjectRepository.ts +0 -707
  447. package/src/adapter/repositories/GraphqlProjectRepositoryProjectLocation.test.ts +0 -297
  448. package/src/adapter/repositories/KyHttpRepository.ts +0 -8
  449. package/src/adapter/repositories/KySlackRepository.test.ts +0 -172
  450. package/src/adapter/repositories/KySlackRepository.ts +0 -298
  451. package/src/adapter/repositories/LocalCommandIssueAttachmentRepository.test.ts +0 -193
  452. package/src/adapter/repositories/LocalCommandIssueAttachmentRepository.ts +0 -106
  453. package/src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.test.ts +0 -190
  454. package/src/adapter/repositories/LocalProcessLiveSessionProcessSnapshotProvider.ts +0 -102
  455. package/src/adapter/repositories/LocalStorageCacheRepository.test.ts +0 -265
  456. package/src/adapter/repositories/LocalStorageCacheRepository.ts +0 -79
  457. package/src/adapter/repositories/LocalStorageRepository.integration.test.ts +0 -142
  458. package/src/adapter/repositories/LocalStorageRepository.test.ts +0 -173
  459. package/src/adapter/repositories/LocalStorageRepository.ts +0 -30
  460. package/src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.test.ts +0 -15
  461. package/src/adapter/repositories/NoUnansweredOwnerCallStatusProvider.ts +0 -9
  462. package/src/adapter/repositories/NodeLocalCommandRunner.largeOutput.test.ts +0 -16
  463. package/src/adapter/repositories/NodeLocalCommandRunner.test.ts +0 -118
  464. package/src/adapter/repositories/NodeLocalCommandRunner.ts +0 -66
  465. package/src/adapter/repositories/NodeProcessSignalRepository.test.ts +0 -82
  466. package/src/adapter/repositories/NodeProcessSignalRepository.ts +0 -47
  467. package/src/adapter/repositories/NodeSubAgentProcessLister.test.ts +0 -44
  468. package/src/adapter/repositories/NodeSubAgentProcessLister.ts +0 -40
  469. package/src/adapter/repositories/NodeTmuxSessionRepository.test.ts +0 -546
  470. package/src/adapter/repositories/NodeTmuxSessionRepository.ts +0 -245
  471. package/src/adapter/repositories/OauthAPIClaudeRepository.ts +0 -279
  472. package/src/adapter/repositories/OauthAPIProxyClaudeRepository.test.ts +0 -166
  473. package/src/adapter/repositories/OauthAPIProxyClaudeRepository.ts +0 -32
  474. package/src/adapter/repositories/OauthProxyClaudeRepository.test.ts +0 -428
  475. package/src/adapter/repositories/OauthProxyClaudeRepository.ts +0 -124
  476. package/src/adapter/repositories/ProcClaudeHandoverSessionRepository.test.ts +0 -242
  477. package/src/adapter/repositories/ProcClaudeHandoverSessionRepository.ts +0 -261
  478. package/src/adapter/repositories/ProcClaudeInteractiveSessionRepository.test.ts +0 -184
  479. package/src/adapter/repositories/ProcClaudeInteractiveSessionRepository.ts +0 -138
  480. package/src/adapter/repositories/ProcClaudeLiveSessionRepository.test.ts +0 -208
  481. package/src/adapter/repositories/ProcClaudeLiveSessionRepository.ts +0 -130
  482. package/src/adapter/repositories/ProcFsProcessEnvironReader.test.ts +0 -60
  483. package/src/adapter/repositories/ProcFsProcessEnvironReader.ts +0 -38
  484. package/src/adapter/repositories/ProcHostMetricsRepository.test.ts +0 -207
  485. package/src/adapter/repositories/ProcHostMetricsRepository.ts +0 -181
  486. package/src/adapter/repositories/ProcTakeOwnershipSpawnRepository.test.ts +0 -231
  487. package/src/adapter/repositories/ProcTakeOwnershipSpawnRepository.ts +0 -134
  488. package/src/adapter/repositories/ProcessListSessionSubAgentActivityRepository.test.ts +0 -121
  489. package/src/adapter/repositories/ProcessListSessionSubAgentActivityRepository.ts +0 -58
  490. package/src/adapter/repositories/ProjectIssuesCacheRepository.test.ts +0 -218
  491. package/src/adapter/repositories/ProjectIssuesCacheRepository.ts +0 -141
  492. package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.test.ts +0 -1195
  493. package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.ts +0 -185
  494. package/src/adapter/repositories/ProxyRateLimitCacheRepository.test.ts +0 -116
  495. package/src/adapter/repositories/ProxyRateLimitCacheRepository.ts +0 -67
  496. package/src/adapter/repositories/RateLimitSnapshotRepository.ts +0 -50
  497. package/src/adapter/repositories/RealSleeper.test.ts +0 -19
  498. package/src/adapter/repositories/RealSleeper.ts +0 -12
  499. package/src/adapter/repositories/RestProjectRepository.test.ts +0 -277
  500. package/src/adapter/repositories/RestProjectRepository.ts +0 -129
  501. package/src/adapter/repositories/SqliteClaudeMessageResponseRepository.test.ts +0 -313
  502. package/src/adapter/repositories/SqliteClaudeMessageResponseRepository.ts +0 -164
  503. package/src/adapter/repositories/SystemDateRepository.test.ts +0 -60
  504. package/src/adapter/repositories/SystemDateRepository.ts +0 -34
  505. package/src/adapter/repositories/TmuxSilentSessionNotificationRepository.test.ts +0 -253
  506. package/src/adapter/repositories/TmuxSilentSessionNotificationRepository.ts +0 -158
  507. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.queuedReply.test.ts +0 -171
  508. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.singleScan.test.ts +0 -86
  509. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.test.ts +0 -996
  510. package/src/adapter/repositories/TranscriptOwnerCallStatusProvider.ts +0 -396
  511. package/src/adapter/repositories/TranscriptRefusalTailStatusProvider.test.ts +0 -208
  512. package/src/adapter/repositories/TranscriptRefusalTailStatusProvider.ts +0 -80
  513. package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.test.ts +0 -1674
  514. package/src/adapter/repositories/TranscriptSessionSubAgentActivityRepository.ts +0 -459
  515. package/src/adapter/repositories/clSessionScopeUnitName.test.ts +0 -76
  516. package/src/adapter/repositories/clSessionScopeUnitName.ts +0 -5
  517. package/src/adapter/repositories/clSessionScopeUnitNameFromCgroupContent.test.ts +0 -34
  518. package/src/adapter/repositories/clSessionScopeUnitNameFromCgroupContent.ts +0 -8
  519. package/src/adapter/repositories/githubGraphqlClient.test.ts +0 -479
  520. package/src/adapter/repositories/githubGraphqlClient.ts +0 -243
  521. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +0 -5182
  522. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +0 -3303
  523. package/src/adapter/repositories/issue/ApiV3IssueRepository.test.ts +0 -46
  524. package/src/adapter/repositories/issue/ApiV3IssueRepository.ts +0 -88
  525. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +0 -1776
  526. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.ts +0 -1552
  527. package/src/adapter/repositories/issue/RestIssueRepository.test.ts +0 -393
  528. package/src/adapter/repositories/issue/RestIssueRepository.ts +0 -252
  529. package/src/adapter/repositories/issue/gitHubRawUrl.test.ts +0 -29
  530. package/src/adapter/repositories/issue/gitHubRawUrl.ts +0 -30
  531. package/src/adapter/repositories/issue/githubRateLimitRetry.test.ts +0 -167
  532. package/src/adapter/repositories/issue/githubRateLimitRetry.ts +0 -109
  533. package/src/adapter/repositories/localStorageCacheDirectory.test.ts +0 -81
  534. package/src/adapter/repositories/localStorageCacheDirectory.ts +0 -13
  535. package/src/adapter/repositories/projectFieldDefinition.test.ts +0 -191
  536. package/src/adapter/repositories/projectFieldDefinition.ts +0 -144
  537. package/src/adapter/repositories/utils.test.ts +0 -55
  538. package/src/adapter/repositories/utils.ts +0 -45
  539. package/src/domain/entities/ClaudeHandoverSession.ts +0 -12
  540. package/src/domain/entities/ClaudeMessageResponse.ts +0 -31
  541. package/src/domain/entities/ClaudeTokenUsage.ts +0 -17
  542. package/src/domain/entities/ClaudeWindowUsage.ts +0 -5
  543. package/src/domain/entities/Comment.ts +0 -5
  544. package/src/domain/entities/InteractiveLiveSession.ts +0 -6
  545. package/src/domain/entities/Issue.ts +0 -31
  546. package/src/domain/entities/KevReportWatermark.ts +0 -4
  547. package/src/domain/entities/LiveSessionActivitySnapshot.ts +0 -14
  548. package/src/domain/entities/LiveSessionOutputActivity.ts +0 -4
  549. package/src/domain/entities/LiveSessionProcessSnapshot.ts +0 -18
  550. package/src/domain/entities/LiveTmuxSession.ts +0 -4
  551. package/src/domain/entities/Member.ts +0 -3
  552. package/src/domain/entities/OutputDegeneration.ts +0 -10
  553. package/src/domain/entities/Project.ts +0 -88
  554. package/src/domain/entities/ProjectField.ts +0 -3
  555. package/src/domain/entities/ProjectFieldName.ts +0 -2
  556. package/src/domain/entities/ProjectFieldSingleSelect.ts +0 -8
  557. package/src/domain/entities/ProjectFieldSingleSelectOption.ts +0 -8
  558. package/src/domain/entities/RequiredProjectField.ts +0 -68
  559. package/src/domain/entities/SearchedIssue.ts +0 -11
  560. package/src/domain/entities/StoryObjectMap.ts +0 -12
  561. package/src/domain/entities/TokenExhaustionHandoverState.ts +0 -8
  562. package/src/domain/entities/WorkflowStatus.ts +0 -66
  563. package/src/domain/usecases/ActionAnnouncementUseCase.test.ts +0 -91
  564. package/src/domain/usecases/ActionAnnouncementUseCase.ts +0 -78
  565. package/src/domain/usecases/AgentDesignationLabelAdoptUseCase.test.ts +0 -222
  566. package/src/domain/usecases/AgentDesignationLabelAdoptUseCase.ts +0 -76
  567. package/src/domain/usecases/AnalyzeProblemByIssueUseCase.test.ts +0 -118
  568. package/src/domain/usecases/AnalyzeProblemByIssueUseCase.ts +0 -112
  569. package/src/domain/usecases/AnalyzeStoriesUseCase.test.ts +0 -82
  570. package/src/domain/usecases/AnalyzeStoriesUseCase.ts +0 -296
  571. package/src/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.test.ts +0 -578
  572. package/src/domain/usecases/AssignNoAssigneeIssueToManagerUseCase.ts +0 -137
  573. package/src/domain/usecases/CONTRIBUTING_FOR_TEST.md +0 -259
  574. package/src/domain/usecases/ChangeStatusByStoryColorUseCase.test.ts +0 -437
  575. package/src/domain/usecases/ChangeStatusByStoryColorUseCase.ts +0 -80
  576. package/src/domain/usecases/ChangeTargetPullRequestApprover.test.ts +0 -248
  577. package/src/domain/usecases/ChangeTargetPullRequestApprover.ts +0 -69
  578. package/src/domain/usecases/CheckIssueReviewReadinessUseCase.test.ts +0 -411
  579. package/src/domain/usecases/CheckIssueReviewReadinessUseCase.ts +0 -142
  580. package/src/domain/usecases/ClearDependedIssueURLUseCase.test.ts +0 -1120
  581. package/src/domain/usecases/ClearDependedIssueURLUseCase.ts +0 -161
  582. package/src/domain/usecases/ClearPastNextActionDateHourUseCase.test.ts +0 -345
  583. package/src/domain/usecases/ClearPastNextActionDateHourUseCase.ts +0 -81
  584. package/src/domain/usecases/CliErrorReportUseCase.test.ts +0 -162
  585. package/src/domain/usecases/CliErrorReportUseCase.ts +0 -72
  586. package/src/domain/usecases/ConflictedIssueRevertUseCase.test.ts +0 -886
  587. package/src/domain/usecases/ConflictedIssueRevertUseCase.ts +0 -146
  588. package/src/domain/usecases/CreateEstimationIssueUseCase.test.ts +0 -227
  589. package/src/domain/usecases/CreateEstimationIssueUseCase.ts +0 -205
  590. package/src/domain/usecases/CreateNewStoryByLabelUseCase.test.ts +0 -1084
  591. package/src/domain/usecases/CreateNewStoryByLabelUseCase.ts +0 -146
  592. package/src/domain/usecases/DailySecurityScanUseCase.test.ts +0 -1709
  593. package/src/domain/usecases/DailySecurityScanUseCase.ts +0 -476
  594. package/src/domain/usecases/DefaultSilentSessionMessageComposer.test.ts +0 -533
  595. package/src/domain/usecases/DefaultSilentSessionMessageComposer.ts +0 -122
  596. package/src/domain/usecases/GetStoryObjectMapUseCase.test.ts +0 -330
  597. package/src/domain/usecases/GetStoryObjectMapUseCase.ts +0 -79
  598. package/src/domain/usecases/HandleScheduledEventUseCase.test.ts +0 -1913
  599. package/src/domain/usecases/HandleScheduledEventUseCase.ts +0 -846
  600. package/src/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.test.ts +0 -175
  601. package/src/domain/usecases/InTmuxByHumanSessionTokenCountUseCase.ts +0 -63
  602. package/src/domain/usecases/IssueNoStatusUpdateUseCase.test.ts +0 -165
  603. package/src/domain/usecases/IssueNoStatusUpdateUseCase.ts +0 -44
  604. package/src/domain/usecases/IssueRejectionEvaluator.test.ts +0 -1137
  605. package/src/domain/usecases/IssueRejectionEvaluator.ts +0 -340
  606. package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.test.ts +0 -679
  607. package/src/domain/usecases/LiveSessionOauthTokenSelectUseCase.ts +0 -178
  608. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +0 -5441
  609. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +0 -891
  610. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.test.ts +0 -2069
  611. package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +0 -489
  612. package/src/domain/usecases/OauthTokenSelectUseCase.test.ts +0 -651
  613. package/src/domain/usecases/OauthTokenSelectUseCase.ts +0 -274
  614. package/src/domain/usecases/OutputDegenerationDetector.test.ts +0 -167
  615. package/src/domain/usecases/OutputDegenerationDetector.ts +0 -169
  616. package/src/domain/usecases/ProjectRequiredFieldCreateUseCase.test.ts +0 -711
  617. package/src/domain/usecases/ProjectRequiredFieldCreateUseCase.ts +0 -146
  618. package/src/domain/usecases/QualityCheckAdvanceUseCase.test.ts +0 -421
  619. package/src/domain/usecases/QualityCheckAdvanceUseCase.ts +0 -76
  620. package/src/domain/usecases/ReopenedDoneIssueRevertUseCase.test.ts +0 -188
  621. package/src/domain/usecases/ReopenedDoneIssueRevertUseCase.ts +0 -57
  622. package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.test.ts +0 -303
  623. package/src/domain/usecases/ResolveInteractiveLiveSessionsUseCase.ts +0 -155
  624. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.test.ts +0 -1838
  625. package/src/domain/usecases/RevertNotReadyReviewQueueIssueUseCase.ts +0 -221
  626. package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +0 -2392
  627. package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +0 -499
  628. package/src/domain/usecases/SessionOutputDegenerationRecoveryUseCase.test.ts +0 -224
  629. package/src/domain/usecases/SessionOutputDegenerationRecoveryUseCase.ts +0 -142
  630. package/src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.test.ts +0 -360
  631. package/src/domain/usecases/SetDependedIssueUrlForOpenTaskPRsUseCase.ts +0 -70
  632. package/src/domain/usecases/SetNoStoryIssueToStoryUseCase.test.ts +0 -400
  633. package/src/domain/usecases/SetNoStoryIssueToStoryUseCase.ts +0 -48
  634. package/src/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.test.ts +0 -696
  635. package/src/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.ts +0 -201
  636. package/src/domain/usecases/SetupTowerDefenceProjectUseCase.test.ts +0 -1363
  637. package/src/domain/usecases/SetupTowerDefenceProjectUseCase.ts +0 -215
  638. package/src/domain/usecases/StaleTaskPullRequestCloseUseCase.test.ts +0 -170
  639. package/src/domain/usecases/StaleTaskPullRequestCloseUseCase.ts +0 -38
  640. package/src/domain/usecases/StaleTmuxSessionKillUseCase.test.ts +0 -283
  641. package/src/domain/usecases/StaleTmuxSessionKillUseCase.ts +0 -98
  642. package/src/domain/usecases/StartPreparationUseCase.test.ts +0 -7822
  643. package/src/domain/usecases/StartPreparationUseCase.ts +0 -876
  644. package/src/domain/usecases/SubscriptionDisabledIssueUseCase.test.ts +0 -200
  645. package/src/domain/usecases/SubscriptionDisabledIssueUseCase.ts +0 -74
  646. package/src/domain/usecases/TokenExhaustionHandoverUseCase.test.ts +0 -838
  647. package/src/domain/usecases/TokenExhaustionHandoverUseCase.ts +0 -439
  648. package/src/domain/usecases/UpdateIssueStatusByLabelUseCase.test.ts +0 -306
  649. package/src/domain/usecases/UpdateIssueStatusByLabelUseCase.ts +0 -64
  650. package/src/domain/usecases/UpdateRateLimitCacheUseCase.test.ts +0 -178
  651. package/src/domain/usecases/UpdateRateLimitCacheUseCase.ts +0 -22
  652. package/src/domain/usecases/adapter-interfaces/ClaudeHandoverSessionRepository.ts +0 -5
  653. package/src/domain/usecases/adapter-interfaces/ClaudeInteractiveSessionRepository.ts +0 -9
  654. package/src/domain/usecases/adapter-interfaces/ClaudeLiveSessionRepository.ts +0 -8
  655. package/src/domain/usecases/adapter-interfaces/ClaudeMessageResponseRepository.ts +0 -5
  656. package/src/domain/usecases/adapter-interfaces/ClaudeRepository.ts +0 -6
  657. package/src/domain/usecases/adapter-interfaces/ClaudeTokenUsageRepository.ts +0 -8
  658. package/src/domain/usecases/adapter-interfaces/ConsoleTabsRepository.ts +0 -12
  659. package/src/domain/usecases/adapter-interfaces/CopilotRepository.ts +0 -3
  660. package/src/domain/usecases/adapter-interfaces/DateRepository.ts +0 -7
  661. package/src/domain/usecases/adapter-interfaces/GitHubGraphqlRateLimitRepository.ts +0 -3
  662. package/src/domain/usecases/adapter-interfaces/HttpRepository.ts +0 -3
  663. package/src/domain/usecases/adapter-interfaces/InteractiveLiveSessionTranscriptResolver.ts +0 -7
  664. package/src/domain/usecases/adapter-interfaces/IssueAttachmentRepository.ts +0 -9
  665. package/src/domain/usecases/adapter-interfaces/IssueCheckpointRepository.ts +0 -3
  666. package/src/domain/usecases/adapter-interfaces/IssueCommentRepository.ts +0 -7
  667. package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +0 -229
  668. package/src/domain/usecases/adapter-interfaces/KevReportWatermarkRepository.ts +0 -11
  669. package/src/domain/usecases/adapter-interfaces/LiveSessionProcessSnapshotProvider.ts +0 -5
  670. package/src/domain/usecases/adapter-interfaces/LocalCommandRunner.ts +0 -16
  671. package/src/domain/usecases/adapter-interfaces/OwnerCallStatusProvider.ts +0 -5
  672. package/src/domain/usecases/adapter-interfaces/ProcessEnvironReader.ts +0 -3
  673. package/src/domain/usecases/adapter-interfaces/ProcessSignalRepository.ts +0 -5
  674. package/src/domain/usecases/adapter-interfaces/ProjectRepository.ts +0 -29
  675. package/src/domain/usecases/adapter-interfaces/RateLimitCacheRepository.ts +0 -10
  676. package/src/domain/usecases/adapter-interfaces/RefusalTailStatusProvider.ts +0 -5
  677. package/src/domain/usecases/adapter-interfaces/SessionAssistantTurnsRepository.ts +0 -6
  678. package/src/domain/usecases/adapter-interfaces/SessionDegenerationCooldownStateRepository.ts +0 -4
  679. package/src/domain/usecases/adapter-interfaces/SessionOutputActivityRepository.ts +0 -7
  680. package/src/domain/usecases/adapter-interfaces/SessionRecordReader.ts +0 -3
  681. package/src/domain/usecases/adapter-interfaces/SessionSubAgentActivityRepository.ts +0 -8
  682. package/src/domain/usecases/adapter-interfaces/SilentSessionCandidateStateRepository.ts +0 -10
  683. package/src/domain/usecases/adapter-interfaces/SilentSessionHubTaskStatusCacheRepository.ts +0 -20
  684. package/src/domain/usecases/adapter-interfaces/SilentSessionMessageComposer.ts +0 -18
  685. package/src/domain/usecases/adapter-interfaces/SilentSessionNotificationRepository.ts +0 -6
  686. package/src/domain/usecases/adapter-interfaces/SlackRepository.ts +0 -20
  687. package/src/domain/usecases/adapter-interfaces/Sleeper.ts +0 -3
  688. package/src/domain/usecases/adapter-interfaces/SpreadsheetRepository.ts +0 -18
  689. package/src/domain/usecases/adapter-interfaces/StatusDefaultRepository.ts +0 -5
  690. package/src/domain/usecases/adapter-interfaces/SubAgentLivenessResolver.ts +0 -6
  691. package/src/domain/usecases/adapter-interfaces/SubAgentProcessLister.ts +0 -8
  692. package/src/domain/usecases/adapter-interfaces/SubAgentSilentSecondsResolver.ts +0 -3
  693. package/src/domain/usecases/adapter-interfaces/SubAgentTranscriptDirectoryResolver.ts +0 -6
  694. package/src/domain/usecases/adapter-interfaces/TakeOwnershipSpawnRepository.ts +0 -9
  695. package/src/domain/usecases/adapter-interfaces/TmuxSessionRepository.ts +0 -20
  696. package/src/domain/usecases/adapter-interfaces/TokenExhaustionHandoverStateRepository.ts +0 -6
  697. package/src/domain/usecases/adapter-interfaces/TokenRateLimitSnapshotRepository.ts +0 -22
  698. package/src/domain/usecases/adapter-interfaces/WebhookRepository.ts +0 -3
  699. package/src/domain/usecases/agentReportPrefix.ts +0 -1
  700. package/src/domain/usecases/autoStatusCheckComments.ts +0 -1
  701. package/src/domain/usecases/console/.gitkeep +0 -0
  702. package/src/domain/usecases/console/GenerateConsoleListsUseCase.test.ts +0 -887
  703. package/src/domain/usecases/console/GenerateConsoleListsUseCase.ts +0 -368
  704. package/src/domain/usecases/dashboard/ComposeDashboardUseCase.test.ts +0 -654
  705. package/src/domain/usecases/dashboard/ComposeDashboardUseCase.ts +0 -318
  706. package/src/domain/usecases/dashboard/DashboardProjectCode.test.ts +0 -34
  707. package/src/domain/usecases/dashboard/DashboardProjectCode.ts +0 -27
  708. package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.test.ts +0 -160
  709. package/src/domain/usecases/dashboard/GenerateDashboardRowUseCase.ts +0 -69
  710. package/src/domain/usecases/dashboard/GenerateTokenStatusUseCase.test.ts +0 -362
  711. package/src/domain/usecases/dashboard/GenerateTokenStatusUseCase.ts +0 -234
  712. package/src/domain/usecases/ensureAgentOptionAndGetId.ts +0 -47
  713. package/src/domain/usecases/extractFencedJsonBlocks.test.ts +0 -40
  714. package/src/domain/usecases/extractFencedJsonBlocks.ts +0 -21
  715. package/src/domain/usecases/extractNextStepAgent.test.ts +0 -58
  716. package/src/domain/usecases/extractNextStepAgent.ts +0 -18
  717. package/src/domain/usecases/extractNextStepAgentFromComments.ts +0 -13
  718. package/src/domain/usecases/findLastAgentReport.test.ts +0 -121
  719. package/src/domain/usecases/findLastAgentReport.ts +0 -14
  720. package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.test.ts +0 -339
  721. package/src/domain/usecases/intmux/GenerateInTmuxByHumanDataUseCase.ts +0 -188
  722. package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.test.ts +0 -389
  723. package/src/domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase.ts +0 -103
  724. package/src/domain/usecases/intmux/OwnerCallFile.test.ts +0 -184
  725. package/src/domain/usecases/intmux/OwnerCallFile.ts +0 -80
  726. package/src/domain/usecases/isAgentReportBody.test.ts +0 -108
  727. package/src/domain/usecases/isAgentReportBody.ts +0 -52
  728. package/src/domain/usecases/isAuthorAuthorizedForAutoStatusCheck.ts +0 -12
  729. package/src/domain/usecases/isHumanComment.test.ts +0 -77
  730. package/src/domain/usecases/isHumanComment.ts +0 -23
  731. package/src/domain/usecases/isRecord.ts +0 -2
  732. package/src/domain/usecases/issueReactivationTriggerIsPending.test.ts +0 -541
  733. package/src/domain/usecases/issueReactivationTriggerIsPending.ts +0 -25
  734. package/src/domain/usecases/nextStepAgentDispatchRepeatedMessage.ts +0 -2
  735. package/src/domain/usecases/normalizeReportBody.test.ts +0 -24
  736. package/src/domain/usecases/normalizeReportBody.ts +0 -2
  737. package/src/domain/usecases/resolveAllowedIssueAuthors.test.ts +0 -51
  738. package/src/domain/usecases/resolveAllowedIssueAuthors.ts +0 -6
  739. package/src/domain/usecases/resolveLabelsAsLlmAgentName.test.ts +0 -42
  740. package/src/domain/usecases/resolveLabelsAsLlmAgentName.ts +0 -6
  741. package/src/domain/usecases/resolveLabelsNotRequiringPullRequest.test.ts +0 -33
  742. package/src/domain/usecases/resolveLabelsNotRequiringPullRequest.ts +0 -7
  743. package/src/domain/usecases/resolveNextStepAgentDispatchRepetition.test.ts +0 -337
  744. package/src/domain/usecases/resolveNextStepAgentDispatchRepetition.ts +0 -133
  745. package/src/domain/usecases/silentSessionReminderSentinel.ts +0 -9
  746. package/src/domain/usecases/triagerAgentName.ts +0 -4
  747. package/src/domain/usecases/utils.test.ts +0 -35
  748. package/src/domain/usecases/utils.ts +0 -6
  749. package/src/index.test.ts +0 -8
  750. package/src/index.ts +0 -10
  751. package/src/releaseConfig.test.ts +0 -40
  752. package/tsconfig.build.json +0 -18
  753. package/tsconfig.json +0 -25
  754. package/types/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.d.ts +0 -13
  755. package/types/adapter/entry-points/handlers/resolveUnansweredOwnerCalls.d.ts.map +0 -1
  756. package/types/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.d.ts +0 -16
  757. package/types/adapter/repositories/FileSystemSilentSessionNotifiedStateRepository.d.ts.map +0 -1
  758. package/types/adapter/repositories/issue/issueTimelineUtils.d.ts +0 -12
  759. package/types/adapter/repositories/issue/issueTimelineUtils.d.ts.map +0 -1
  760. package/types/domain/entities/UnansweredOwnerCall.d.ts +0 -5
  761. package/types/domain/entities/UnansweredOwnerCall.d.ts.map +0 -1
  762. package/types/domain/entities/WorkingTime.d.ts +0 -8
  763. package/types/domain/entities/WorkingTime.d.ts.map +0 -1
  764. package/types/domain/usecases/ChangeStatusLongInReviewIssueUseCase.d.ts +0 -17
  765. package/types/domain/usecases/ChangeStatusLongInReviewIssueUseCase.d.ts.map +0 -1
  766. package/types/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.d.ts +0 -23
  767. package/types/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.d.ts.map +0 -1
  768. package/types/domain/usecases/GenerateWorkingTimeReportUseCase.d.ts +0 -50
  769. package/types/domain/usecases/GenerateWorkingTimeReportUseCase.d.ts.map +0 -1
  770. package/types/domain/usecases/TriagerApprovalDispatchUseCase.d.ts +0 -15
  771. package/types/domain/usecases/TriagerApprovalDispatchUseCase.d.ts.map +0 -1
  772. package/types/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.d.ts +0 -11
  773. package/types/domain/usecases/adapter-interfaces/SilentSessionNotifiedStateRepository.d.ts.map +0 -1
  774. package/types/domain/usecases/awaitingOwnerApprovalMessage.d.ts +0 -3
  775. package/types/domain/usecases/awaitingOwnerApprovalMessage.d.ts.map +0 -1
  776. package/types/domain/usecases/findLastAgentDeclaringReport.d.ts +0 -5
  777. package/types/domain/usecases/findLastAgentDeclaringReport.d.ts.map +0 -1
  778. package/types/domain/usecases/isPullRequestDeclaredUnnecessary.d.ts +0 -5
  779. package/types/domain/usecases/isPullRequestDeclaredUnnecessary.d.ts.map +0 -1
  780. package/types/domain/usecases/isWaitingForOwnerApproval.d.ts +0 -2
  781. package/types/domain/usecases/isWaitingForOwnerApproval.d.ts.map +0 -1
  782. package/types/domain/usecases/returnedToAwaitingWorkspaceMessage.d.ts +0 -3
  783. 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
- }