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,2837 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as os from 'os';
3
- import * as path from 'path';
4
- import YAML from 'yaml';
5
-
6
- jest.mock('fs', () => {
7
- const actualFs: typeof fs = jest.requireActual('fs');
8
- return {
9
- ...actualFs,
10
- readFileSync: jest.fn(actualFs.readFileSync),
11
- existsSync: jest.fn(actualFs.existsSync),
12
- };
13
- });
14
- import {
15
- program,
16
- loadConfigFile,
17
- parseProjectReadmeConfig,
18
- mergeConfigs,
19
- fetchProjectReadme,
20
- runCliProgram,
21
- reportFatalErrorAndExit,
22
- } from './index';
23
- import { StartPreparationUseCase } from '../../../domain/usecases/StartPreparationUseCase';
24
- import { NotifyFinishedIssuePreparationUseCase } from '../../../domain/usecases/NotifyFinishedIssuePreparationUseCase';
25
- import { CheckIssueReviewReadinessUseCase } from '../../../domain/usecases/CheckIssueReviewReadinessUseCase';
26
- import { ownerCallFileRelativePath } from '../../../domain/usecases/intmux/OwnerCallFile';
27
- import { toTmuxSessionName } from '../../../domain/usecases/intmux/InTmuxByHumanSessionReconcileUseCase';
28
-
29
- jest.mock('../../../domain/usecases/StartPreparationUseCase');
30
- jest.mock('../../../domain/usecases/NotifyFinishedIssuePreparationUseCase');
31
- jest.mock('../../../domain/usecases/CheckIssueReviewReadinessUseCase');
32
- jest.mock('../../repositories/LocalStorageRepository', () => ({
33
- LocalStorageRepository: jest.fn().mockImplementation(() => ({})),
34
- }));
35
- jest.mock('../../repositories/LocalStorageCacheRepository', () => ({
36
- LocalStorageCacheRepository: jest.fn().mockImplementation(() => ({})),
37
- }));
38
- jest.mock('../../repositories/GraphqlProjectRepository', () => ({
39
- GraphqlProjectRepository: jest.fn().mockImplementation(() => ({
40
- findProjectIdByUrl: jest.fn().mockResolvedValue('PVT_kwHOtest456'),
41
- getProject: jest.fn().mockResolvedValue({
42
- id: 'PVT_kwHOtest456',
43
- url: 'https://github.com/orgs/test/projects/1',
44
- databaseId: 1,
45
- name: 'test-project',
46
- status: { name: 'Status', fieldId: 'statusField', statuses: [] },
47
- nextActionDate: null,
48
- nextActionHour: null,
49
- story: null,
50
- remainingEstimationMinutes: null,
51
- dependedIssueUrlSeparatedByComma: null,
52
- completionDate50PercentConfidence: null,
53
- }),
54
- })),
55
- }));
56
- jest.mock('../../repositories/issue/ApiV3IssueRepository', () => ({
57
- ApiV3IssueRepository: jest.fn().mockImplementation(() => ({})),
58
- }));
59
- jest.mock('../../repositories/issue/RestIssueRepository', () => ({
60
- RestIssueRepository: jest.fn().mockImplementation(() => ({})),
61
- }));
62
- jest.mock('../../repositories/issue/GraphqlProjectItemRepository', () => ({
63
- GraphqlProjectItemRepository: jest.fn().mockImplementation(() => ({})),
64
- }));
65
- jest.mock('../../repositories/issue/ApiV3CheerioRestIssueRepository', () => ({
66
- ApiV3CheerioRestIssueRepository: jest.fn().mockImplementation(() => ({
67
- getCachedProject: jest.fn().mockResolvedValue(null),
68
- })),
69
- }));
70
- const mockRunCommand = jest.fn<
71
- Promise<{ stdout: string; stderr: string; exitCode: number }>,
72
- [string, string[]]
73
- >();
74
- const mockSpawnInteractive = jest.fn<void, [string, string[]]>();
75
- jest.mock('../../repositories/NodeLocalCommandRunner', () => ({
76
- NodeLocalCommandRunner: jest.fn().mockImplementation(() => ({
77
- runCommand: mockRunCommand,
78
- spawnInteractive: mockSpawnInteractive,
79
- })),
80
- }));
81
- jest.mock('../../repositories/OauthAPIClaudeRepository', () => ({
82
- OauthAPIClaudeRepository: jest.fn().mockImplementation(() => ({
83
- getUsage: jest.fn(),
84
- isClaudeAvailable: jest.fn(),
85
- })),
86
- }));
87
- jest.mock('../../repositories/GitHubIssueCommentRepository', () => ({
88
- GitHubIssueCommentRepository: jest.fn().mockImplementation(() => ({})),
89
- }));
90
- jest.mock('../../repositories/FetchWebhookRepository', () => ({
91
- FetchWebhookRepository: jest.fn().mockImplementation(() => ({
92
- sendGetRequest: jest.fn(),
93
- })),
94
- }));
95
- const mockScheduleHandle = jest.fn().mockResolvedValue(null);
96
- jest.mock('../handlers/HandleScheduledEventUseCaseHandler', () => ({
97
- HandleScheduledEventUseCaseHandler: jest.fn().mockImplementation(() => ({
98
- handle: mockScheduleHandle,
99
- })),
100
- }));
101
- jest.mock('../console/ensureConsoleRunning', () => ({
102
- ensureConsoleRunning: jest.fn().mockResolvedValue(null),
103
- }));
104
- import * as ensureConsoleRunningModule from '../console/ensureConsoleRunning';
105
- import { GraphqlProjectRepository } from '../../repositories/GraphqlProjectRepository';
106
-
107
- import type { StartWebServerOptions } from '../console/webServer';
108
-
109
- const mockStartWebServer = jest
110
- .fn<Promise<unknown>, [StartWebServerOptions]>()
111
- .mockResolvedValue({
112
- close: jest.fn(),
113
- address: jest.fn().mockReturnValue({ port: 9980 }),
114
- });
115
- jest.mock('../console/webServer', () => {
116
- const actual: Record<string, unknown> = jest.requireActual(
117
- '../console/webServer',
118
- );
119
- return {
120
- ...actual,
121
- startWebServer: (options: StartWebServerOptions): Promise<unknown> =>
122
- mockStartWebServer(options),
123
- };
124
- });
125
-
126
- describe('CLI', () => {
127
- const originalEnv = process.env;
128
- const tmpDir = path.join(__dirname, '../../../../tmp/test-cli');
129
- const configFilePath = path.join(tmpDir, 'config.yml');
130
- const cacheHomeDir = path.join(tmpDir, 'cache-home');
131
-
132
- const defaultConfig = {
133
- projectUrl: 'https://github.com/orgs/test/projects/1',
134
- defaultAgentName: 'agent1',
135
- projectName: 'test-project',
136
- manager: 'test-manager',
137
- };
138
-
139
- const writeConfig = (config: Record<string, unknown>): void => {
140
- fs.writeFileSync(configFilePath, YAML.stringify(config));
141
- };
142
-
143
- const mockFetchReturningReadme = (readme: string | null): void => {
144
- const responseBody =
145
- readme === null
146
- ? { data: {} }
147
- : { data: { organization: { projectV2: { readme } } } };
148
- jest.spyOn(global, 'fetch').mockResolvedValue(
149
- new Response(JSON.stringify(responseBody), {
150
- status: 200,
151
- headers: { 'Content-Type': 'application/json' },
152
- }),
153
- );
154
- };
155
-
156
- beforeAll(() => {
157
- if (!fs.existsSync(tmpDir)) {
158
- fs.mkdirSync(tmpDir, { recursive: true });
159
- }
160
- });
161
-
162
- afterAll(() => {
163
- if (fs.existsSync(configFilePath)) {
164
- fs.unlinkSync(configFilePath);
165
- }
166
- if (fs.existsSync(cacheHomeDir)) {
167
- fs.rmSync(cacheHomeDir, { recursive: true, force: true });
168
- }
169
- if (fs.existsSync(tmpDir)) {
170
- fs.rmdirSync(tmpDir);
171
- }
172
- });
173
-
174
- beforeEach(() => {
175
- jest.clearAllMocks();
176
- mockFetchReturningReadme(null);
177
- mockRunCommand.mockResolvedValue({ stdout: '', stderr: '', exitCode: 0 });
178
- process.env = {
179
- ...originalEnv,
180
- GH_TOKEN: 'test-token',
181
- XDG_CACHE_HOME: cacheHomeDir,
182
- };
183
- writeConfig(defaultConfig);
184
- });
185
-
186
- afterEach(() => {
187
- process.env = originalEnv;
188
- });
189
-
190
- it('should export program', () => {
191
- expect(program).toBeDefined();
192
- });
193
-
194
- describe('schedule --inTmuxProjectOrder', () => {
195
- it('should pass the parsed, trimmed project order to the handler', async () => {
196
- await program.parseAsync([
197
- 'node',
198
- 'test',
199
- 'schedule',
200
- '-t',
201
- 'schedule',
202
- '-c',
203
- configFilePath,
204
- '--inTmuxProjectOrder',
205
- 'alpha, beta ,gamma',
206
- ]);
207
-
208
- expect(mockScheduleHandle).toHaveBeenCalledWith(
209
- configFilePath,
210
- undefined,
211
- ['alpha', 'beta', 'gamma'],
212
- );
213
- });
214
-
215
- it('should pass null when --inTmuxProjectOrder is omitted', async () => {
216
- await program.parseAsync([
217
- 'node',
218
- 'test',
219
- 'schedule',
220
- '-t',
221
- 'schedule',
222
- '-c',
223
- configFilePath,
224
- ]);
225
-
226
- expect(mockScheduleHandle).toHaveBeenCalledWith(
227
- configFilePath,
228
- undefined,
229
- null,
230
- );
231
- });
232
- });
233
-
234
- describe('loadConfigFile', () => {
235
- it('should load config from YAML file', () => {
236
- const config = {
237
- projectUrl: 'https://github.com/orgs/test/projects/1',
238
- defaultAgentName: 'agent1',
239
- defaultLlmModelName: 'claude-opus-4-5',
240
- defaultLlmAgentName: 'aw',
241
- maximumPreparingIssuesCount: 10,
242
- utilizationPercentageThreshold: 80,
243
- allowedIssueAuthors: 'user1,user2',
244
- thresholdForAutoReject: 5,
245
- workflowBlockerResolvedWebhookUrl: 'https://example.com/webhook',
246
- projectName: 'test-project',
247
- codexHomeCandidates: ['.codex-dev1', '.codex-main'],
248
- };
249
- writeConfig(config);
250
-
251
- const result = loadConfigFile(configFilePath);
252
-
253
- expect(result).toEqual(config);
254
- });
255
-
256
- it('should load codexHomeCandidates string array from config file', () => {
257
- const config = {
258
- ...defaultConfig,
259
- codexHomeCandidates: ['.codex-dev1', '.codex-dev2', '.codex-main'],
260
- };
261
- writeConfig(config);
262
-
263
- const result = loadConfigFile(configFilePath);
264
-
265
- expect(result.codexHomeCandidates).toEqual([
266
- '.codex-dev1',
267
- '.codex-dev2',
268
- '.codex-main',
269
- ]);
270
- });
271
-
272
- it('should return undefined codexHomeCandidates when not in config', () => {
273
- writeConfig(defaultConfig);
274
-
275
- const result = loadConfigFile(configFilePath);
276
-
277
- expect(result.codexHomeCandidates).toBeUndefined();
278
- });
279
-
280
- it('should return undefined codexHomeCandidates when array contains non-string items', () => {
281
- const config = {
282
- ...defaultConfig,
283
- codexHomeCandidates: ['.codex-dev1', 123],
284
- };
285
- writeConfig(config);
286
-
287
- const result = loadConfigFile(configFilePath);
288
-
289
- expect(result.codexHomeCandidates).toBeUndefined();
290
- });
291
-
292
- it('should return empty config for empty YAML', () => {
293
- fs.writeFileSync(configFilePath, '');
294
-
295
- const result = loadConfigFile(configFilePath);
296
-
297
- expect(result).toEqual({});
298
- });
299
-
300
- it('should ignore non-string values for string fields', () => {
301
- const config = {
302
- projectUrl: 123,
303
- defaultAgentName: true,
304
- };
305
- writeConfig(config);
306
-
307
- const result = loadConfigFile(configFilePath);
308
-
309
- expect(result.projectUrl).toBeUndefined();
310
- expect(result.defaultAgentName).toBeUndefined();
311
- });
312
-
313
- it('should ignore non-number values for number fields', () => {
314
- const config = {
315
- maximumPreparingIssuesCount: 'abc',
316
- utilizationPercentageThreshold: 'ghi',
317
- thresholdForAutoReject: 'jkl',
318
- };
319
- writeConfig(config);
320
-
321
- const result = loadConfigFile(configFilePath);
322
-
323
- expect(result.maximumPreparingIssuesCount).toBeUndefined();
324
- expect(result.utilizationPercentageThreshold).toBeUndefined();
325
- expect(result.thresholdForAutoReject).toBeUndefined();
326
- });
327
-
328
- it('should return empty config for array YAML', () => {
329
- fs.writeFileSync(configFilePath, '- item1\n- item2\n');
330
-
331
- const result = loadConfigFile(configFilePath);
332
-
333
- expect(result).toEqual({});
334
- });
335
-
336
- it('should exit with error when config file does not exist', () => {
337
- const nonExistentPath = path.join(tmpDir, 'nonexistent-config.yml');
338
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
339
- const processExitSpy = jest
340
- .spyOn(process, 'exit')
341
- .mockImplementation(() => {
342
- throw new Error('process.exit called');
343
- });
344
-
345
- expect(() => loadConfigFile(nonExistentPath)).toThrow(
346
- 'process.exit called',
347
- );
348
-
349
- expect(consoleErrorSpy).toHaveBeenCalledWith(
350
- expect.stringContaining(
351
- `Failed to load configuration file "${nonExistentPath}"`,
352
- ),
353
- );
354
- expect(processExitSpy).toHaveBeenCalledWith(1);
355
-
356
- consoleErrorSpy.mockRestore();
357
- processExitSpy.mockRestore();
358
- });
359
- });
360
-
361
- describe('parseProjectReadmeConfig', () => {
362
- it('should parse YAML from details/summary config section', () => {
363
- const readme = `# Project
364
- Some description
365
- <details>
366
- <summary>config</summary>
367
- defaultAgentName: 'readme-agent'
368
- defaultLlmModelName: 'claude-opus-4-5'
369
- </details>`;
370
-
371
- const result = parseProjectReadmeConfig(readme);
372
-
373
- expect(result.defaultAgentName).toBe('readme-agent');
374
- expect(result.defaultLlmModelName).toBe('claude-opus-4-5');
375
- });
376
-
377
- it('should return empty config when no details/summary section exists', () => {
378
- const readme = '# Project\nSome description without config section';
379
-
380
- const result = parseProjectReadmeConfig(readme);
381
-
382
- expect(result).toEqual({});
383
- });
384
-
385
- it('should return empty config when details section has empty content', () => {
386
- const readme = '<details>\n<summary>config</summary>\n</details>';
387
-
388
- const result = parseProjectReadmeConfig(readme);
389
-
390
- expect(result).toEqual({});
391
- });
392
-
393
- it('should return empty config when YAML content is not a record', () => {
394
- const readme =
395
- '<details>\n<summary>config</summary>\n- item1\n- item2\n</details>';
396
-
397
- const result = parseProjectReadmeConfig(readme);
398
-
399
- expect(result).toEqual({});
400
- });
401
-
402
- it('should handle invalid YAML gracefully', () => {
403
- const readme =
404
- '<details>\n<summary>config</summary>\ninvalid: [unclosed\n</details>';
405
- const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
406
-
407
- const result = parseProjectReadmeConfig(readme);
408
-
409
- expect(result).toEqual({});
410
- expect(consoleWarnSpy).toHaveBeenCalledWith(
411
- 'Failed to parse YAML from project README config section',
412
- );
413
-
414
- consoleWarnSpy.mockRestore();
415
- });
416
-
417
- it('should parse number fields from README config', () => {
418
- const readme = `<details>
419
- <summary>config</summary>
420
- maximumPreparingIssuesCount: 15
421
- utilizationPercentageThreshold: 80
422
- thresholdForAutoReject: 5
423
- </details>`;
424
-
425
- const result = parseProjectReadmeConfig(readme);
426
-
427
- expect(result.maximumPreparingIssuesCount).toBe(15);
428
- expect(result.utilizationPercentageThreshold).toBe(80);
429
- expect(result.thresholdForAutoReject).toBe(5);
430
- });
431
-
432
- it('should be case-insensitive for the summary tag', () => {
433
- const readme = `<details>
434
- <SUMMARY>config</SUMMARY>
435
- defaultAgentName: 'case-test-agent'
436
- </details>`;
437
-
438
- const result = parseProjectReadmeConfig(readme);
439
-
440
- expect(result.defaultAgentName).toBe('case-test-agent');
441
- });
442
-
443
- it('should parse codexHomeCandidates string array from README config', () => {
444
- const readme = `<details>
445
- <summary>config</summary>
446
- codexHomeCandidates:
447
- - .codex-dev1
448
- - .codex-dev2
449
- - .codex-main
450
- </details>`;
451
-
452
- const result = parseProjectReadmeConfig(readme);
453
-
454
- expect(result.codexHomeCandidates).toEqual([
455
- '.codex-dev1',
456
- '.codex-dev2',
457
- '.codex-main',
458
- ]);
459
- });
460
-
461
- it('should parse changeTargetPathAliases map from README config', () => {
462
- const readme = `<details>
463
- <summary>config</summary>
464
- changeTargetPathAliases:
465
- adapters: src/domain/usecases/adapter-interfaces
466
- domain: src/domain
467
- </details>`;
468
-
469
- const result = parseProjectReadmeConfig(readme);
470
-
471
- expect(result.changeTargetPathAliases).toEqual({
472
- adapters: 'src/domain/usecases/adapter-interfaces',
473
- domain: 'src/domain',
474
- });
475
- });
476
-
477
- it('should not warn when only known top-level keys are present', () => {
478
- const readme = `<details>
479
- <summary>config</summary>
480
- defaultAgentName: 'agent'
481
- maximumPreparingIssuesCount: 5
482
- </details>`;
483
- const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
484
-
485
- const result = parseProjectReadmeConfig(
486
- readme,
487
- 'https://github.com/orgs/example/projects/1',
488
- );
489
-
490
- expect(result.defaultAgentName).toBe('agent');
491
- expect(result.maximumPreparingIssuesCount).toBe(5);
492
- expect(consoleWarnSpy).not.toHaveBeenCalled();
493
-
494
- consoleWarnSpy.mockRestore();
495
- });
496
-
497
- it('should warn for each unknown top-level key with project URL', () => {
498
- const readme = `<details>
499
- <summary>config</summary>
500
- unknownOne: 'value-one'
501
- unknownTwo: 42
502
- </details>`;
503
- const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
504
-
505
- parseProjectReadmeConfig(
506
- readme,
507
- 'https://github.com/orgs/example/projects/1',
508
- );
509
-
510
- expect(consoleWarnSpy).toHaveBeenCalledWith(
511
- 'Unknown key "unknownOne" in project README config section (project URL: https://github.com/orgs/example/projects/1)',
512
- );
513
- expect(consoleWarnSpy).toHaveBeenCalledWith(
514
- 'Unknown key "unknownTwo" in project README config section (project URL: https://github.com/orgs/example/projects/1)',
515
- );
516
- expect(consoleWarnSpy).toHaveBeenCalledTimes(2);
517
-
518
- consoleWarnSpy.mockRestore();
519
- });
520
-
521
- it('should warn only for unknown keys in a mixed config', () => {
522
- const readme = `<details>
523
- <summary>config</summary>
524
- defaultAgentName: 'agent'
525
- unexpectedKey: 'oops'
526
- </details>`;
527
- const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
528
-
529
- const result = parseProjectReadmeConfig(
530
- readme,
531
- 'https://github.com/orgs/example/projects/2',
532
- );
533
-
534
- expect(result.defaultAgentName).toBe('agent');
535
- expect(consoleWarnSpy).toHaveBeenCalledTimes(1);
536
- expect(consoleWarnSpy).toHaveBeenCalledWith(
537
- 'Unknown key "unexpectedKey" in project README config section (project URL: https://github.com/orgs/example/projects/2)',
538
- );
539
-
540
- consoleWarnSpy.mockRestore();
541
- });
542
-
543
- it('should warn for a typo of a known key (maximumPreparingIssueCount missing trailing s)', () => {
544
- const readme = `<details>
545
- <summary>config</summary>
546
- maximumPreparingIssueCount: 9
547
- </details>`;
548
- const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
549
-
550
- const result = parseProjectReadmeConfig(
551
- readme,
552
- 'https://github.com/orgs/example/projects/3',
553
- );
554
-
555
- expect(result.maximumPreparingIssuesCount).toBeUndefined();
556
- expect(consoleWarnSpy).toHaveBeenCalledWith(
557
- 'Unknown key "maximumPreparingIssueCount" in project README config section (project URL: https://github.com/orgs/example/projects/3)',
558
- );
559
-
560
- consoleWarnSpy.mockRestore();
561
- });
562
-
563
- it('should warn without project URL suffix when projectUrl is not provided', () => {
564
- const readme = `<details>
565
- <summary>config</summary>
566
- mysteryKey: 'value'
567
- </details>`;
568
- const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
569
-
570
- parseProjectReadmeConfig(readme);
571
-
572
- expect(consoleWarnSpy).toHaveBeenCalledWith(
573
- 'Unknown key "mysteryKey" in project README config section',
574
- );
575
-
576
- consoleWarnSpy.mockRestore();
577
- });
578
- });
579
-
580
- describe('mergeConfigs', () => {
581
- it('should use configFile values when no overrides', () => {
582
- const configFile = {
583
- projectUrl: 'https://github.com/config/project',
584
- defaultAgentName: 'config-agent',
585
- };
586
-
587
- const result = mergeConfigs(configFile, {}, {});
588
-
589
- expect(result.projectUrl).toBe('https://github.com/config/project');
590
- expect(result.defaultAgentName).toBe('config-agent');
591
- });
592
-
593
- it('should use CLI overrides over configFile', () => {
594
- const configFile = {
595
- projectUrl: 'https://github.com/config/project',
596
- defaultAgentName: 'config-agent',
597
- };
598
- const cliOverrides = {
599
- defaultAgentName: 'cli-agent',
600
- };
601
-
602
- const result = mergeConfigs(configFile, cliOverrides, {});
603
-
604
- expect(result.projectUrl).toBe('https://github.com/config/project');
605
- expect(result.defaultAgentName).toBe('cli-agent');
606
- });
607
-
608
- it('should use README overrides over both CLI and configFile', () => {
609
- const configFile = {
610
- projectUrl: 'https://github.com/config/project',
611
- defaultAgentName: 'config-agent',
612
- };
613
- const cliOverrides = {
614
- defaultAgentName: 'cli-agent',
615
- };
616
- const readmeOverrides = {
617
- defaultAgentName: 'readme-agent',
618
- };
619
-
620
- const result = mergeConfigs(configFile, cliOverrides, readmeOverrides);
621
-
622
- expect(result.projectUrl).toBe('https://github.com/config/project');
623
- expect(result.defaultAgentName).toBe('readme-agent');
624
- });
625
-
626
- it('should preserve projectName from configFile', () => {
627
- const configFile = {
628
- projectName: 'my-project',
629
- projectUrl: 'https://github.com/config/project',
630
- };
631
-
632
- const result = mergeConfigs(configFile, {}, {});
633
-
634
- expect(result.projectName).toBe('my-project');
635
- });
636
- });
637
-
638
- describe('fetchProjectReadme', () => {
639
- it('should fetch README from GitHub GraphQL API', async () => {
640
- const responseBody = {
641
- data: {
642
- organization: {
643
- projectV2: { readme: '# Project README' },
644
- },
645
- },
646
- };
647
- jest
648
- .spyOn(global, 'fetch')
649
- .mockResolvedValue(
650
- new Response(JSON.stringify(responseBody), { status: 200 }),
651
- );
652
-
653
- const result = await fetchProjectReadme(
654
- 'https://github.com/orgs/test-org/projects/1',
655
- 'test-token',
656
- );
657
-
658
- expect(result).toBe('# Project README');
659
- });
660
-
661
- it('should try user if organization readme is null', async () => {
662
- const responseBody = {
663
- data: {
664
- organization: { projectV2: { readme: null } },
665
- user: { projectV2: { readme: '# User Project README' } },
666
- },
667
- };
668
- jest
669
- .spyOn(global, 'fetch')
670
- .mockResolvedValue(
671
- new Response(JSON.stringify(responseBody), { status: 200 }),
672
- );
673
-
674
- const result = await fetchProjectReadme(
675
- 'https://github.com/users/test-user/projects/1',
676
- 'test-token',
677
- );
678
-
679
- expect(result).toBe('# User Project README');
680
- });
681
-
682
- it('should fetch README for user-owned project when organization is null in response', async () => {
683
- const responseBody = {
684
- data: {
685
- organization: null,
686
- user: { projectV2: { readme: '# User Owned Project README' } },
687
- },
688
- errors: [
689
- {
690
- type: 'NOT_FOUND',
691
- message:
692
- "Could not resolve to an Organization with the login of 'test-user'.",
693
- },
694
- ],
695
- };
696
- jest
697
- .spyOn(global, 'fetch')
698
- .mockResolvedValue(
699
- new Response(JSON.stringify(responseBody), { status: 200 }),
700
- );
701
-
702
- const result = await fetchProjectReadme(
703
- 'https://github.com/users/test-user/projects/1',
704
- 'test-token',
705
- );
706
-
707
- expect(result).toBe('# User Owned Project README');
708
- });
709
-
710
- it('should return null and warn on failure', async () => {
711
- jest.spyOn(global, 'fetch').mockRejectedValue(new Error('Network error'));
712
-
713
- const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
714
-
715
- const result = await fetchProjectReadme(
716
- 'https://github.com/orgs/test/projects/1',
717
- 'test-token',
718
- );
719
-
720
- expect(result).toBeNull();
721
- expect(consoleWarnSpy).toHaveBeenCalledWith(
722
- expect.stringContaining('Failed to fetch project README'),
723
- );
724
-
725
- consoleWarnSpy.mockRestore();
726
- });
727
-
728
- it('should reflect maximumPreparingIssuesCount from user-owned project README in merged config', async () => {
729
- const readmeWithConfig = [
730
- '# User Project',
731
- '<details>',
732
- '<summary>config</summary>',
733
- 'maximumPreparingIssuesCount: 7',
734
- '</details>',
735
- ].join('\n');
736
- const responseBody = {
737
- data: {
738
- organization: null,
739
- user: { projectV2: { readme: readmeWithConfig } },
740
- },
741
- errors: [
742
- {
743
- type: 'NOT_FOUND',
744
- message:
745
- "Could not resolve to an Organization with the login of 'test-user'.",
746
- },
747
- ],
748
- };
749
- jest
750
- .spyOn(global, 'fetch')
751
- .mockResolvedValue(
752
- new Response(JSON.stringify(responseBody), { status: 200 }),
753
- );
754
-
755
- const readme = await fetchProjectReadme(
756
- 'https://github.com/users/test-user/projects/1',
757
- 'test-token',
758
- );
759
- const readmeOverrides = parseProjectReadmeConfig(readme ?? '');
760
- const merged = mergeConfigs({}, {}, readmeOverrides);
761
-
762
- expect(merged.maximumPreparingIssuesCount).toBe(7);
763
- });
764
- });
765
-
766
- describe('startDaemon', () => {
767
- it('should read parameters from config file', async () => {
768
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
769
- const MockedStartPreparationUseCase = jest.mocked(
770
- StartPreparationUseCase,
771
- );
772
-
773
- MockedStartPreparationUseCase.mockImplementation(function (
774
- this: StartPreparationUseCase,
775
- ) {
776
- this.run = mockRun;
777
- return this;
778
- });
779
-
780
- await program.parseAsync([
781
- 'node',
782
- 'test',
783
- 'startDaemon',
784
- '--configFilePath',
785
- configFilePath,
786
- ]);
787
-
788
- expect(mockRun).toHaveBeenCalledTimes(1);
789
- expect(mockRun).toHaveBeenCalledWith({
790
- projectUrl: 'https://github.com/orgs/test/projects/1',
791
- defaultAgentName: 'agent1',
792
- defaultLlmModelName: null,
793
- fallbackLlmModelName: null,
794
- defaultLlmAgentName: null,
795
- configFilePath: configFilePath,
796
- maximumPreparingIssuesCount: null,
797
- utilizationPercentageThreshold: 90,
798
- allowedIssueAuthors: null,
799
- manager: 'test-manager',
800
- codexHomeCandidates: null,
801
- labelsAsLlmAgentName: null,
802
- agents: null,
803
- normalConcurrentLimit: 6,
804
- maxConcurrentWorkers: 40,
805
- graphqlRateLimitFloor: 500,
806
- });
807
- });
808
-
809
- it('should pass the configured agent definitions to start preparation', async () => {
810
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
811
- const MockedStartPreparationUseCase = jest.mocked(
812
- StartPreparationUseCase,
813
- );
814
-
815
- MockedStartPreparationUseCase.mockImplementation(function (
816
- this: StartPreparationUseCase,
817
- ) {
818
- this.run = mockRun;
819
- return this;
820
- });
821
- writeConfig({
822
- ...defaultConfig,
823
- agents: ['impl', 'chore', 'accounting'],
824
- });
825
-
826
- await program.parseAsync([
827
- 'node',
828
- 'test',
829
- 'startDaemon',
830
- '--configFilePath',
831
- configFilePath,
832
- ]);
833
-
834
- expect(mockRun).toHaveBeenCalledTimes(1);
835
- expect(mockRun).toHaveBeenCalledWith(
836
- expect.objectContaining({ agents: ['impl', 'chore', 'accounting'] }),
837
- );
838
- });
839
-
840
- it('should allow CLI args to override config file values', async () => {
841
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
842
- const MockedStartPreparationUseCase = jest.mocked(
843
- StartPreparationUseCase,
844
- );
845
-
846
- MockedStartPreparationUseCase.mockImplementation(function (
847
- this: StartPreparationUseCase,
848
- ) {
849
- this.run = mockRun;
850
- return this;
851
- });
852
-
853
- await program.parseAsync([
854
- 'node',
855
- 'test',
856
- 'startDaemon',
857
- '--configFilePath',
858
- configFilePath,
859
- '--projectUrl',
860
- 'https://github.com/orgs/override/projects/2',
861
- '--defaultAgentName',
862
- 'override-agent',
863
- ]);
864
-
865
- expect(mockRun).toHaveBeenCalledTimes(1);
866
- expect(mockRun).toHaveBeenCalledWith({
867
- projectUrl: 'https://github.com/orgs/override/projects/2',
868
- defaultAgentName: 'override-agent',
869
- defaultLlmModelName: null,
870
- fallbackLlmModelName: null,
871
- defaultLlmAgentName: null,
872
- configFilePath: configFilePath,
873
- maximumPreparingIssuesCount: null,
874
- utilizationPercentageThreshold: 90,
875
- allowedIssueAuthors: null,
876
- manager: 'test-manager',
877
- codexHomeCandidates: null,
878
- labelsAsLlmAgentName: null,
879
- agents: null,
880
- normalConcurrentLimit: 6,
881
- maxConcurrentWorkers: 40,
882
- graphqlRateLimitFloor: 500,
883
- });
884
- });
885
-
886
- it('should pass defaultLlmModelName and allowedIssueAuthors from config file', async () => {
887
- const configWithLlm = {
888
- ...defaultConfig,
889
- defaultLlmModelName: 'claude-opus-4-5',
890
- allowedIssueAuthors: 'user1,user2',
891
- };
892
- writeConfig(configWithLlm);
893
-
894
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
895
- const MockedStartPreparationUseCase = jest.mocked(
896
- StartPreparationUseCase,
897
- );
898
-
899
- MockedStartPreparationUseCase.mockImplementation(function (
900
- this: StartPreparationUseCase,
901
- ) {
902
- this.run = mockRun;
903
- return this;
904
- });
905
-
906
- await program.parseAsync([
907
- 'node',
908
- 'test',
909
- 'startDaemon',
910
- '--configFilePath',
911
- configFilePath,
912
- ]);
913
-
914
- expect(mockRun).toHaveBeenCalledWith(
915
- expect.objectContaining({
916
- defaultLlmModelName: 'claude-opus-4-5',
917
- allowedIssueAuthors: ['user1', 'user2'],
918
- }),
919
- );
920
- });
921
-
922
- it('should pass maximumPreparingIssuesCount from config file', async () => {
923
- const configWithCount = {
924
- ...defaultConfig,
925
- maximumPreparingIssuesCount: 10,
926
- };
927
- writeConfig(configWithCount);
928
-
929
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
930
- const MockedStartPreparationUseCase = jest.mocked(
931
- StartPreparationUseCase,
932
- );
933
-
934
- MockedStartPreparationUseCase.mockImplementation(function (
935
- this: StartPreparationUseCase,
936
- ) {
937
- this.run = mockRun;
938
- return this;
939
- });
940
-
941
- await program.parseAsync([
942
- 'node',
943
- 'test',
944
- 'startDaemon',
945
- '--configFilePath',
946
- configFilePath,
947
- ]);
948
-
949
- expect(mockRun).toHaveBeenCalledWith(
950
- expect.objectContaining({
951
- maximumPreparingIssuesCount: 10,
952
- }),
953
- );
954
- });
955
-
956
- it('should pass maximumPreparingIssuesCount from CLI overriding config', async () => {
957
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
958
- const MockedStartPreparationUseCase = jest.mocked(
959
- StartPreparationUseCase,
960
- );
961
-
962
- MockedStartPreparationUseCase.mockImplementation(function (
963
- this: StartPreparationUseCase,
964
- ) {
965
- this.run = mockRun;
966
- return this;
967
- });
968
-
969
- await program.parseAsync([
970
- 'node',
971
- 'test',
972
- 'startDaemon',
973
- '--configFilePath',
974
- configFilePath,
975
- '--maximumPreparingIssuesCount',
976
- '20',
977
- ]);
978
-
979
- expect(mockRun).toHaveBeenCalledWith(
980
- expect.objectContaining({
981
- maximumPreparingIssuesCount: 20,
982
- }),
983
- );
984
- });
985
-
986
- it('should exit with error for non-numeric maximumPreparingIssuesCount', async () => {
987
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
988
- const processExitSpy = jest
989
- .spyOn(process, 'exit')
990
- .mockImplementation(() => {
991
- throw new Error('process.exit called');
992
- });
993
-
994
- await expect(
995
- program.parseAsync([
996
- 'node',
997
- 'test',
998
- 'startDaemon',
999
- '--configFilePath',
1000
- configFilePath,
1001
- '--maximumPreparingIssuesCount',
1002
- 'abc',
1003
- ]),
1004
- ).rejects.toThrow('process.exit called');
1005
-
1006
- expect(consoleErrorSpy).toHaveBeenCalledWith(
1007
- 'Invalid value for --maximumPreparingIssuesCount. It must be a positive integer.',
1008
- );
1009
- expect(processExitSpy).toHaveBeenCalledWith(1);
1010
-
1011
- consoleErrorSpy.mockRestore();
1012
- processExitSpy.mockRestore();
1013
- });
1014
-
1015
- it('should exit with error for negative maximumPreparingIssuesCount', async () => {
1016
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
1017
- const processExitSpy = jest
1018
- .spyOn(process, 'exit')
1019
- .mockImplementation(() => {
1020
- throw new Error('process.exit called');
1021
- });
1022
-
1023
- await expect(
1024
- program.parseAsync([
1025
- 'node',
1026
- 'test',
1027
- 'startDaemon',
1028
- '--configFilePath',
1029
- configFilePath,
1030
- '--maximumPreparingIssuesCount',
1031
- '-5',
1032
- ]),
1033
- ).rejects.toThrow('process.exit called');
1034
-
1035
- expect(consoleErrorSpy).toHaveBeenCalledWith(
1036
- 'Invalid value for --maximumPreparingIssuesCount. It must be a positive integer.',
1037
- );
1038
- expect(processExitSpy).toHaveBeenCalledWith(1);
1039
-
1040
- consoleErrorSpy.mockRestore();
1041
- processExitSpy.mockRestore();
1042
- });
1043
-
1044
- it('should exit with error when GH_TOKEN is missing', async () => {
1045
- delete process.env.GH_TOKEN;
1046
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
1047
- const processExitSpy = jest
1048
- .spyOn(process, 'exit')
1049
- .mockImplementation(() => {
1050
- throw new Error('process.exit called');
1051
- });
1052
-
1053
- await expect(
1054
- program.parseAsync([
1055
- 'node',
1056
- 'test',
1057
- 'startDaemon',
1058
- '--configFilePath',
1059
- configFilePath,
1060
- ]),
1061
- ).rejects.toThrow('process.exit called');
1062
-
1063
- expect(consoleErrorSpy).toHaveBeenCalledWith(
1064
- 'GH_TOKEN environment variable is required',
1065
- );
1066
- expect(processExitSpy).toHaveBeenCalledWith(1);
1067
-
1068
- consoleErrorSpy.mockRestore();
1069
- processExitSpy.mockRestore();
1070
- });
1071
-
1072
- it('should exit with error when projectUrl is missing from both CLI and config', async () => {
1073
- const configWithoutProjectUrl = {
1074
- defaultAgentName: 'agent1',
1075
- };
1076
- writeConfig(configWithoutProjectUrl);
1077
-
1078
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
1079
- const processExitSpy = jest
1080
- .spyOn(process, 'exit')
1081
- .mockImplementation(() => {
1082
- throw new Error('process.exit called');
1083
- });
1084
-
1085
- await expect(
1086
- program.parseAsync([
1087
- 'node',
1088
- 'test',
1089
- 'startDaemon',
1090
- '--configFilePath',
1091
- configFilePath,
1092
- ]),
1093
- ).rejects.toThrow('process.exit called');
1094
-
1095
- expect(consoleErrorSpy).toHaveBeenCalledWith(
1096
- 'projectUrl is required. Provide via --projectUrl, config file, or project README.',
1097
- );
1098
- expect(processExitSpy).toHaveBeenCalledWith(1);
1099
-
1100
- consoleErrorSpy.mockRestore();
1101
- processExitSpy.mockRestore();
1102
- });
1103
-
1104
- it('should exit with error when defaultAgentName is missing', async () => {
1105
- const configMissing = {
1106
- projectUrl: 'https://github.com/orgs/test/projects/1',
1107
- };
1108
- writeConfig(configMissing);
1109
-
1110
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
1111
- const processExitSpy = jest
1112
- .spyOn(process, 'exit')
1113
- .mockImplementation(() => {
1114
- throw new Error('process.exit called');
1115
- });
1116
-
1117
- await expect(
1118
- program.parseAsync([
1119
- 'node',
1120
- 'test',
1121
- 'startDaemon',
1122
- '--configFilePath',
1123
- configFilePath,
1124
- ]),
1125
- ).rejects.toThrow('process.exit called');
1126
-
1127
- expect(consoleErrorSpy).toHaveBeenCalledWith(
1128
- 'defaultAgentName is required. Provide via --defaultAgentName, config file, or project README.',
1129
- );
1130
- expect(processExitSpy).toHaveBeenCalledWith(1);
1131
-
1132
- consoleErrorSpy.mockRestore();
1133
- processExitSpy.mockRestore();
1134
- });
1135
-
1136
- it('should exit with error when manager is missing', async () => {
1137
- const configMissing = {
1138
- projectUrl: 'https://github.com/orgs/test/projects/1',
1139
- defaultAgentName: 'agent1',
1140
- };
1141
- writeConfig(configMissing);
1142
-
1143
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
1144
- const processExitSpy = jest
1145
- .spyOn(process, 'exit')
1146
- .mockImplementation(() => {
1147
- throw new Error('process.exit called');
1148
- });
1149
-
1150
- await expect(
1151
- program.parseAsync([
1152
- 'node',
1153
- 'test',
1154
- 'startDaemon',
1155
- '--configFilePath',
1156
- configFilePath,
1157
- ]),
1158
- ).rejects.toThrow('process.exit called');
1159
-
1160
- expect(consoleErrorSpy).toHaveBeenCalledWith(
1161
- 'manager is required. Provide via the config file so that only issues assigned to the manager are picked up.',
1162
- );
1163
- expect(processExitSpy).toHaveBeenCalledWith(1);
1164
-
1165
- consoleErrorSpy.mockRestore();
1166
- processExitSpy.mockRestore();
1167
- });
1168
-
1169
- it('should log maximumPreparingIssuesCount before calling useCase.run', async () => {
1170
- const configWithValues = {
1171
- ...defaultConfig,
1172
- maximumPreparingIssuesCount: 10,
1173
- };
1174
- writeConfig(configWithValues);
1175
-
1176
- const callOrder: string[] = [];
1177
- const consoleLogSpy = jest
1178
- .spyOn(console, 'log')
1179
- .mockImplementation(() => {
1180
- callOrder.push('console.log');
1181
- });
1182
- const mockRun = jest.fn().mockImplementation(() => {
1183
- callOrder.push('useCase.run');
1184
- return Promise.resolve({ rotationOrder: null });
1185
- });
1186
- const MockedStartPreparationUseCase = jest.mocked(
1187
- StartPreparationUseCase,
1188
- );
1189
-
1190
- MockedStartPreparationUseCase.mockImplementation(function (
1191
- this: StartPreparationUseCase,
1192
- ) {
1193
- this.run = mockRun;
1194
- return this;
1195
- });
1196
-
1197
- await program.parseAsync([
1198
- 'node',
1199
- 'test',
1200
- 'startDaemon',
1201
- '--configFilePath',
1202
- configFilePath,
1203
- ]);
1204
-
1205
- expect(consoleLogSpy).toHaveBeenCalledWith(
1206
- 'maximumPreparingIssuesCount: 10',
1207
- );
1208
- expect(mockRun).toHaveBeenCalledTimes(1);
1209
- const logIndex = callOrder.indexOf('console.log');
1210
- const runIndex = callOrder.indexOf('useCase.run');
1211
- expect(logIndex).toBeLessThan(runIndex);
1212
-
1213
- consoleLogSpy.mockRestore();
1214
- });
1215
-
1216
- it('should apply README config overrides', async () => {
1217
- const readmeContent = [
1218
- '# Project',
1219
- '<details>',
1220
- '<summary>config</summary>',
1221
- 'defaultAgentName: readme-agent',
1222
- '</details>',
1223
- ].join('\n');
1224
- mockFetchReturningReadme(readmeContent);
1225
-
1226
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
1227
- const MockedStartPreparationUseCase = jest.mocked(
1228
- StartPreparationUseCase,
1229
- );
1230
-
1231
- MockedStartPreparationUseCase.mockImplementation(function (
1232
- this: StartPreparationUseCase,
1233
- ) {
1234
- this.run = mockRun;
1235
- return this;
1236
- });
1237
-
1238
- await program.parseAsync([
1239
- 'node',
1240
- 'test',
1241
- 'startDaemon',
1242
- '--configFilePath',
1243
- configFilePath,
1244
- ]);
1245
-
1246
- expect(mockRun).toHaveBeenCalledWith(
1247
- expect.objectContaining({
1248
- defaultAgentName: 'readme-agent',
1249
- }),
1250
- );
1251
- });
1252
-
1253
- it('should pass codexHomeCandidates from config file', async () => {
1254
- const configWithCandidates = {
1255
- ...defaultConfig,
1256
- codexHomeCandidates: ['.codex-dev1', '.codex-dev2', '.codex-main'],
1257
- };
1258
- writeConfig(configWithCandidates);
1259
-
1260
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
1261
- const MockedStartPreparationUseCase = jest.mocked(
1262
- StartPreparationUseCase,
1263
- );
1264
-
1265
- MockedStartPreparationUseCase.mockImplementation(function (
1266
- this: StartPreparationUseCase,
1267
- ) {
1268
- this.run = mockRun;
1269
- return this;
1270
- });
1271
-
1272
- await program.parseAsync([
1273
- 'node',
1274
- 'test',
1275
- 'startDaemon',
1276
- '--configFilePath',
1277
- configFilePath,
1278
- ]);
1279
-
1280
- expect(mockRun).toHaveBeenCalledWith(
1281
- expect.objectContaining({
1282
- codexHomeCandidates: ['.codex-dev1', '.codex-dev2', '.codex-main'],
1283
- }),
1284
- );
1285
- });
1286
-
1287
- it('should pass codexHomeCandidates as null when not in config', async () => {
1288
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
1289
- const MockedStartPreparationUseCase = jest.mocked(
1290
- StartPreparationUseCase,
1291
- );
1292
-
1293
- MockedStartPreparationUseCase.mockImplementation(function (
1294
- this: StartPreparationUseCase,
1295
- ) {
1296
- this.run = mockRun;
1297
- return this;
1298
- });
1299
-
1300
- await program.parseAsync([
1301
- 'node',
1302
- 'test',
1303
- 'startDaemon',
1304
- '--configFilePath',
1305
- configFilePath,
1306
- ]);
1307
-
1308
- expect(mockRun).toHaveBeenCalledWith(
1309
- expect.objectContaining({
1310
- codexHomeCandidates: null,
1311
- }),
1312
- );
1313
- });
1314
-
1315
- it('should pass codexHomeCandidates from README config', async () => {
1316
- const readmeContent = [
1317
- '# Project',
1318
- '<details>',
1319
- '<summary>config</summary>',
1320
- 'codexHomeCandidates:',
1321
- ' - .codex-readme1',
1322
- ' - .codex-readme2',
1323
- '</details>',
1324
- ].join('\n');
1325
- mockFetchReturningReadme(readmeContent);
1326
-
1327
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
1328
- const MockedStartPreparationUseCase = jest.mocked(
1329
- StartPreparationUseCase,
1330
- );
1331
-
1332
- MockedStartPreparationUseCase.mockImplementation(function (
1333
- this: StartPreparationUseCase,
1334
- ) {
1335
- this.run = mockRun;
1336
- return this;
1337
- });
1338
-
1339
- await program.parseAsync([
1340
- 'node',
1341
- 'test',
1342
- 'startDaemon',
1343
- '--configFilePath',
1344
- configFilePath,
1345
- ]);
1346
-
1347
- expect(mockRun).toHaveBeenCalledWith(
1348
- expect.objectContaining({
1349
- codexHomeCandidates: ['.codex-readme1', '.codex-readme2'],
1350
- }),
1351
- );
1352
- });
1353
-
1354
- it('should start web console before preparation cycle when consoleAccessToken is provided', async () => {
1355
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
1356
- const MockedStartPreparationUseCase = jest.mocked(
1357
- StartPreparationUseCase,
1358
- );
1359
- MockedStartPreparationUseCase.mockImplementation(function (
1360
- this: StartPreparationUseCase,
1361
- ) {
1362
- this.run = mockRun;
1363
- return this;
1364
- });
1365
-
1366
- const mockKill = jest.fn();
1367
- const callOrder: string[] = [];
1368
- jest
1369
- .mocked(ensureConsoleRunningModule.ensureConsoleRunning)
1370
- .mockImplementationOnce(async () => {
1371
- callOrder.push('ensureWebConsoleRunning');
1372
- return { kill: mockKill };
1373
- });
1374
- mockRun.mockImplementationOnce(async () => {
1375
- callOrder.push('preparationRun');
1376
- return { rotationOrder: null };
1377
- });
1378
-
1379
- writeConfig({
1380
- ...defaultConfig,
1381
- consoleAccessToken: 'test-key-abc',
1382
- consoleProjects: {
1383
- acme: 'https://github.com/orgs/acme/projects/1',
1384
- globex: 'https://github.com/orgs/globex/projects/1',
1385
- },
1386
- });
1387
-
1388
- await program.parseAsync([
1389
- 'node',
1390
- 'test',
1391
- 'startDaemon',
1392
- '--configFilePath',
1393
- configFilePath,
1394
- ]);
1395
-
1396
- expect(callOrder).toEqual(['ensureWebConsoleRunning', 'preparationRun']);
1397
- expect(
1398
- ensureConsoleRunningModule.ensureConsoleRunning,
1399
- ).toHaveBeenCalledWith(configFilePath, 9980, ['acme', 'globex']);
1400
- });
1401
-
1402
- it('should not start web console when consoleAccessToken is not provided', async () => {
1403
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
1404
- const MockedStartPreparationUseCase = jest.mocked(
1405
- StartPreparationUseCase,
1406
- );
1407
- MockedStartPreparationUseCase.mockImplementation(function (
1408
- this: StartPreparationUseCase,
1409
- ) {
1410
- this.run = mockRun;
1411
- return this;
1412
- });
1413
-
1414
- jest.mocked(ensureConsoleRunningModule.ensureConsoleRunning).mockClear();
1415
-
1416
- await program.parseAsync([
1417
- 'node',
1418
- 'test',
1419
- 'startDaemon',
1420
- '--configFilePath',
1421
- configFilePath,
1422
- ]);
1423
-
1424
- expect(
1425
- ensureConsoleRunningModule.ensureConsoleRunning,
1426
- ).not.toHaveBeenCalled();
1427
- });
1428
-
1429
- it('should register SIGTERM and SIGINT handlers when a new web console process is started', async () => {
1430
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
1431
- const MockedStartPreparationUseCase = jest.mocked(
1432
- StartPreparationUseCase,
1433
- );
1434
- MockedStartPreparationUseCase.mockImplementation(function (
1435
- this: StartPreparationUseCase,
1436
- ) {
1437
- this.run = mockRun;
1438
- return this;
1439
- });
1440
-
1441
- const mockKill = jest.fn();
1442
- jest
1443
- .mocked(ensureConsoleRunningModule.ensureConsoleRunning)
1444
- .mockResolvedValueOnce({ kill: mockKill });
1445
-
1446
- const processOnceSpy = jest.spyOn(process, 'once');
1447
- const processExitSpy = jest
1448
- .spyOn(process, 'exit')
1449
- .mockImplementation(() => {
1450
- throw new Error('process.exit called');
1451
- });
1452
-
1453
- writeConfig({ ...defaultConfig, consoleAccessToken: 'test-key-sigterm' });
1454
-
1455
- await program.parseAsync([
1456
- 'node',
1457
- 'test',
1458
- 'startDaemon',
1459
- '--configFilePath',
1460
- configFilePath,
1461
- ]);
1462
-
1463
- const sigtermCall = processOnceSpy.mock.calls.find(
1464
- ([event]) => event === 'SIGTERM',
1465
- );
1466
- const sigintCall = processOnceSpy.mock.calls.find(
1467
- ([event]) => event === 'SIGINT',
1468
- );
1469
- expect(sigtermCall).toBeDefined();
1470
- expect(sigintCall).toBeDefined();
1471
-
1472
- processOnceSpy.mockRestore();
1473
-
1474
- const rawSigtermHandler = sigtermCall?.[1];
1475
- const rawSigintHandler = sigintCall?.[1];
1476
- if (typeof rawSigtermHandler !== 'function') {
1477
- throw new Error('Expected SIGTERM handler to be a function');
1478
- }
1479
- if (typeof rawSigintHandler !== 'function') {
1480
- throw new Error('Expected SIGINT handler to be a function');
1481
- }
1482
- expect(() => rawSigtermHandler()).toThrow('process.exit called');
1483
- expect(mockKill).toHaveBeenCalledTimes(1);
1484
- expect(processExitSpy).toHaveBeenCalledWith(0);
1485
- expect(() => rawSigintHandler()).toThrow('process.exit called');
1486
- expect(mockKill).toHaveBeenCalledTimes(2);
1487
- expect(processExitSpy).toHaveBeenCalledTimes(2);
1488
-
1489
- processExitSpy.mockRestore();
1490
- });
1491
- });
1492
-
1493
- describe('notifyFinishedIssuePreparation', () => {
1494
- it('should read parameters from config file', async () => {
1495
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
1496
- const MockedNotifyFinishedUseCase = jest.mocked(
1497
- NotifyFinishedIssuePreparationUseCase,
1498
- );
1499
-
1500
- MockedNotifyFinishedUseCase.mockImplementation(function (
1501
- this: NotifyFinishedIssuePreparationUseCase,
1502
- ) {
1503
- this.run = mockRun;
1504
- return this;
1505
- });
1506
-
1507
- await program.parseAsync([
1508
- 'node',
1509
- 'test',
1510
- 'notifyFinishedIssuePreparation',
1511
- '--configFilePath',
1512
- configFilePath,
1513
- '--issueUrl',
1514
- 'https://github.com/test/repo/issues/1',
1515
- ]);
1516
-
1517
- expect(mockRun).toHaveBeenCalledTimes(1);
1518
- expect(mockRun).toHaveBeenCalledWith({
1519
- projectUrl: 'https://github.com/orgs/test/projects/1',
1520
- issueUrl: 'https://github.com/test/repo/issues/1',
1521
- thresholdForAutoReject: 3,
1522
- thresholdForDispatchLoop: 6,
1523
- workflowBlockerResolvedWebhookUrl: null,
1524
- allowedIssueAuthors: null,
1525
- labelsAsLlmAgentName: null,
1526
- labelsNotRequiringPullRequest: null,
1527
- changeTargetPathAliases: null,
1528
- agents: null,
1529
- missingAgentName: null,
1530
- sessionErrorLine: null,
1531
- manager: 'test-manager',
1532
- developerAgentNames: null,
1533
- deferPreparation: null,
1534
- });
1535
- });
1536
-
1537
- it('should allow CLI args to override config file values', async () => {
1538
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
1539
- const MockedNotifyFinishedUseCase = jest.mocked(
1540
- NotifyFinishedIssuePreparationUseCase,
1541
- );
1542
-
1543
- MockedNotifyFinishedUseCase.mockImplementation(function (
1544
- this: NotifyFinishedIssuePreparationUseCase,
1545
- ) {
1546
- this.run = mockRun;
1547
- return this;
1548
- });
1549
-
1550
- await program.parseAsync([
1551
- 'node',
1552
- 'test',
1553
- 'notifyFinishedIssuePreparation',
1554
- '--configFilePath',
1555
- configFilePath,
1556
- '--issueUrl',
1557
- 'https://github.com/test/repo/issues/1',
1558
- '--projectUrl',
1559
- 'https://github.com/orgs/override/projects/2',
1560
- ]);
1561
-
1562
- expect(mockRun).toHaveBeenCalledWith({
1563
- projectUrl: 'https://github.com/orgs/override/projects/2',
1564
- issueUrl: 'https://github.com/test/repo/issues/1',
1565
- thresholdForAutoReject: 3,
1566
- thresholdForDispatchLoop: 6,
1567
- workflowBlockerResolvedWebhookUrl: null,
1568
- allowedIssueAuthors: null,
1569
- labelsAsLlmAgentName: null,
1570
- labelsNotRequiringPullRequest: null,
1571
- changeTargetPathAliases: null,
1572
- agents: null,
1573
- missingAgentName: null,
1574
- sessionErrorLine: null,
1575
- manager: 'test-manager',
1576
- developerAgentNames: null,
1577
- deferPreparation: null,
1578
- });
1579
- });
1580
-
1581
- it('should pass custom thresholdForAutoReject from config file', async () => {
1582
- const configWithThreshold = {
1583
- ...defaultConfig,
1584
- thresholdForAutoReject: 5,
1585
- };
1586
- writeConfig(configWithThreshold);
1587
-
1588
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
1589
- const MockedNotifyFinishedUseCase = jest.mocked(
1590
- NotifyFinishedIssuePreparationUseCase,
1591
- );
1592
-
1593
- MockedNotifyFinishedUseCase.mockImplementation(function (
1594
- this: NotifyFinishedIssuePreparationUseCase,
1595
- ) {
1596
- this.run = mockRun;
1597
- return this;
1598
- });
1599
-
1600
- await program.parseAsync([
1601
- 'node',
1602
- 'test',
1603
- 'notifyFinishedIssuePreparation',
1604
- '--configFilePath',
1605
- configFilePath,
1606
- '--issueUrl',
1607
- 'https://github.com/test/repo/issues/1',
1608
- ]);
1609
-
1610
- expect(mockRun).toHaveBeenCalledWith(
1611
- expect.objectContaining({
1612
- thresholdForAutoReject: 5,
1613
- }),
1614
- );
1615
- });
1616
-
1617
- it('should pass custom thresholdForAutoReject from CLI overriding config', async () => {
1618
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
1619
- const MockedNotifyFinishedUseCase = jest.mocked(
1620
- NotifyFinishedIssuePreparationUseCase,
1621
- );
1622
-
1623
- MockedNotifyFinishedUseCase.mockImplementation(function (
1624
- this: NotifyFinishedIssuePreparationUseCase,
1625
- ) {
1626
- this.run = mockRun;
1627
- return this;
1628
- });
1629
-
1630
- await program.parseAsync([
1631
- 'node',
1632
- 'test',
1633
- 'notifyFinishedIssuePreparation',
1634
- '--configFilePath',
1635
- configFilePath,
1636
- '--issueUrl',
1637
- 'https://github.com/test/repo/issues/1',
1638
- '--thresholdForAutoReject',
1639
- '7',
1640
- ]);
1641
-
1642
- expect(mockRun).toHaveBeenCalledWith(
1643
- expect.objectContaining({
1644
- thresholdForAutoReject: 7,
1645
- }),
1646
- );
1647
- });
1648
-
1649
- it('should exit with error for invalid thresholdForAutoReject', async () => {
1650
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
1651
- const processExitSpy = jest
1652
- .spyOn(process, 'exit')
1653
- .mockImplementation(() => {
1654
- throw new Error('process.exit called');
1655
- });
1656
-
1657
- await expect(
1658
- program.parseAsync([
1659
- 'node',
1660
- 'test',
1661
- 'notifyFinishedIssuePreparation',
1662
- '--configFilePath',
1663
- configFilePath,
1664
- '--issueUrl',
1665
- 'https://github.com/test/repo/issues/1',
1666
- '--thresholdForAutoReject',
1667
- 'abc',
1668
- ]),
1669
- ).rejects.toThrow('process.exit called');
1670
-
1671
- expect(consoleErrorSpy).toHaveBeenCalledWith(
1672
- 'Invalid value for --thresholdForAutoReject. It must be a positive integer.',
1673
- );
1674
- expect(processExitSpy).toHaveBeenCalledWith(1);
1675
-
1676
- consoleErrorSpy.mockRestore();
1677
- processExitSpy.mockRestore();
1678
- });
1679
-
1680
- it('should exit with error when GH_TOKEN is missing', async () => {
1681
- delete process.env.GH_TOKEN;
1682
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
1683
- const processExitSpy = jest
1684
- .spyOn(process, 'exit')
1685
- .mockImplementation(() => {
1686
- throw new Error('process.exit called');
1687
- });
1688
-
1689
- await expect(
1690
- program.parseAsync([
1691
- 'node',
1692
- 'test',
1693
- 'notifyFinishedIssuePreparation',
1694
- '--configFilePath',
1695
- configFilePath,
1696
- '--issueUrl',
1697
- 'https://github.com/test/repo/issues/1',
1698
- ]),
1699
- ).rejects.toThrow('process.exit called');
1700
-
1701
- expect(consoleErrorSpy).toHaveBeenCalledWith(
1702
- 'GH_TOKEN environment variable is required',
1703
- );
1704
- expect(processExitSpy).toHaveBeenCalledWith(1);
1705
-
1706
- consoleErrorSpy.mockRestore();
1707
- processExitSpy.mockRestore();
1708
- });
1709
-
1710
- it('should exit with error when projectUrl is missing', async () => {
1711
- const configMissing = {};
1712
- writeConfig(configMissing);
1713
-
1714
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
1715
- const processExitSpy = jest
1716
- .spyOn(process, 'exit')
1717
- .mockImplementation(() => {
1718
- throw new Error('process.exit called');
1719
- });
1720
-
1721
- await expect(
1722
- program.parseAsync([
1723
- 'node',
1724
- 'test',
1725
- 'notifyFinishedIssuePreparation',
1726
- '--configFilePath',
1727
- configFilePath,
1728
- '--issueUrl',
1729
- 'https://github.com/test/repo/issues/1',
1730
- ]),
1731
- ).rejects.toThrow('process.exit called');
1732
-
1733
- expect(consoleErrorSpy).toHaveBeenCalledWith(
1734
- 'projectUrl is required. Provide via --projectUrl, config file, or project README.',
1735
- );
1736
- expect(processExitSpy).toHaveBeenCalledWith(1);
1737
-
1738
- consoleErrorSpy.mockRestore();
1739
- processExitSpy.mockRestore();
1740
- });
1741
-
1742
- it('should pass workflowBlockerResolvedWebhookUrl from config file', async () => {
1743
- const configWithWebhook = {
1744
- ...defaultConfig,
1745
- workflowBlockerResolvedWebhookUrl:
1746
- 'https://example.com/webhook?url={URL}',
1747
- };
1748
- writeConfig(configWithWebhook);
1749
-
1750
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
1751
- const MockedNotifyFinishedUseCase = jest.mocked(
1752
- NotifyFinishedIssuePreparationUseCase,
1753
- );
1754
-
1755
- MockedNotifyFinishedUseCase.mockImplementation(function (
1756
- this: NotifyFinishedIssuePreparationUseCase,
1757
- ) {
1758
- this.run = mockRun;
1759
- return this;
1760
- });
1761
-
1762
- await program.parseAsync([
1763
- 'node',
1764
- 'test',
1765
- 'notifyFinishedIssuePreparation',
1766
- '--configFilePath',
1767
- configFilePath,
1768
- '--issueUrl',
1769
- 'https://github.com/test/repo/issues/1',
1770
- ]);
1771
-
1772
- expect(mockRun).toHaveBeenCalledWith(
1773
- expect.objectContaining({
1774
- workflowBlockerResolvedWebhookUrl:
1775
- 'https://example.com/webhook?url={URL}',
1776
- }),
1777
- );
1778
- });
1779
-
1780
- it('should apply README config overrides for thresholdForAutoReject', async () => {
1781
- const readmeContent = [
1782
- '# Project',
1783
- '<details>',
1784
- '<summary>config</summary>',
1785
- 'thresholdForAutoReject: 9',
1786
- '</details>',
1787
- ].join('\n');
1788
- mockFetchReturningReadme(readmeContent);
1789
-
1790
- const mockRun = jest.fn().mockResolvedValue({ rotationOrder: null });
1791
- const MockedNotifyFinishedUseCase = jest.mocked(
1792
- NotifyFinishedIssuePreparationUseCase,
1793
- );
1794
-
1795
- MockedNotifyFinishedUseCase.mockImplementation(function (
1796
- this: NotifyFinishedIssuePreparationUseCase,
1797
- ) {
1798
- this.run = mockRun;
1799
- return this;
1800
- });
1801
-
1802
- await program.parseAsync([
1803
- 'node',
1804
- 'test',
1805
- 'notifyFinishedIssuePreparation',
1806
- '--configFilePath',
1807
- configFilePath,
1808
- '--issueUrl',
1809
- 'https://github.com/test/repo/issues/1',
1810
- ]);
1811
-
1812
- expect(mockRun).toHaveBeenCalledWith(
1813
- expect.objectContaining({
1814
- thresholdForAutoReject: 9,
1815
- }),
1816
- );
1817
- });
1818
-
1819
- it('passes manager from config file to useCase.run', async () => {
1820
- const configWithManager = {
1821
- ...defaultConfig,
1822
- manager: 'alice',
1823
- };
1824
- writeConfig(configWithManager);
1825
-
1826
- const mockRun = jest.fn().mockResolvedValue(undefined);
1827
- const MockedNotifyFinishedUseCase = jest.mocked(
1828
- NotifyFinishedIssuePreparationUseCase,
1829
- );
1830
-
1831
- MockedNotifyFinishedUseCase.mockImplementation(function (
1832
- this: NotifyFinishedIssuePreparationUseCase,
1833
- ) {
1834
- this.run = mockRun;
1835
- return this;
1836
- });
1837
-
1838
- await program.parseAsync([
1839
- 'node',
1840
- 'test',
1841
- 'notifyFinishedIssuePreparation',
1842
- '--configFilePath',
1843
- configFilePath,
1844
- '--issueUrl',
1845
- 'https://github.com/test/repo/issues/1',
1846
- ]);
1847
-
1848
- expect(mockRun).toHaveBeenCalledWith(
1849
- expect.objectContaining({ manager: 'alice' }),
1850
- );
1851
- });
1852
- });
1853
-
1854
- describe('checkIssueReviewReadiness', () => {
1855
- it('should appear in the CLI help output', () => {
1856
- const helpText = program.helpInformation();
1857
- expect(helpText).toContain('checkIssueReviewReadiness');
1858
- });
1859
-
1860
- it('should write reviewReady=true JSON to stdout when the use case reports zero rejections', async () => {
1861
- const mockRun = jest
1862
- .fn()
1863
- .mockResolvedValue({ reviewReady: true, rejections: [] });
1864
- const MockedCheckUseCase = jest.mocked(CheckIssueReviewReadinessUseCase);
1865
-
1866
- MockedCheckUseCase.mockImplementation(function (
1867
- this: CheckIssueReviewReadinessUseCase,
1868
- ) {
1869
- this.run = mockRun;
1870
- return this;
1871
- });
1872
-
1873
- const stdoutSpy = jest
1874
- .spyOn(process.stdout, 'write')
1875
- .mockImplementation(() => true);
1876
-
1877
- await program.parseAsync([
1878
- 'node',
1879
- 'test',
1880
- 'checkIssueReviewReadiness',
1881
- '--configFilePath',
1882
- configFilePath,
1883
- '--issueUrl',
1884
- 'https://github.com/test/repo/issues/1',
1885
- ]);
1886
-
1887
- expect(mockRun).toHaveBeenCalledTimes(1);
1888
- expect(mockRun).toHaveBeenCalledWith({
1889
- issueUrl: 'https://github.com/test/repo/issues/1',
1890
- allowedIssueAuthors: null,
1891
- labelsAsLlmAgentName: null,
1892
- labelsNotRequiringPullRequest: null,
1893
- developerAgentNames: null,
1894
- });
1895
- expect(stdoutSpy).toHaveBeenCalledWith(
1896
- `${JSON.stringify({ reviewReady: true, rejections: [] })}\n`,
1897
- );
1898
-
1899
- stdoutSpy.mockRestore();
1900
- });
1901
-
1902
- it('should write reviewReady=false JSON with rejections to stdout when the use case reports rejections', async () => {
1903
- const rejections = [
1904
- {
1905
- type: 'ANY_CI_JOB_FAILED_OR_IN_PROGRESS',
1906
- detail:
1907
- 'ANY_CI_JOB_FAILED_OR_IN_PROGRESS: https://github.com/test/repo/pull/1',
1908
- },
1909
- ];
1910
- const mockRun = jest
1911
- .fn()
1912
- .mockResolvedValue({ reviewReady: false, rejections });
1913
- const MockedCheckUseCase = jest.mocked(CheckIssueReviewReadinessUseCase);
1914
-
1915
- MockedCheckUseCase.mockImplementation(function (
1916
- this: CheckIssueReviewReadinessUseCase,
1917
- ) {
1918
- this.run = mockRun;
1919
- return this;
1920
- });
1921
-
1922
- const stdoutSpy = jest
1923
- .spyOn(process.stdout, 'write')
1924
- .mockImplementation(() => true);
1925
-
1926
- await program.parseAsync([
1927
- 'node',
1928
- 'test',
1929
- 'checkIssueReviewReadiness',
1930
- '--configFilePath',
1931
- configFilePath,
1932
- '--issueUrl',
1933
- 'https://github.com/test/repo/issues/1',
1934
- ]);
1935
-
1936
- expect(mockRun).toHaveBeenCalledTimes(1);
1937
- expect(stdoutSpy).toHaveBeenCalledWith(
1938
- `${JSON.stringify({ reviewReady: false, rejections })}\n`,
1939
- );
1940
-
1941
- stdoutSpy.mockRestore();
1942
- });
1943
-
1944
- it('should work without --projectUrl when config has no projectUrl', async () => {
1945
- const configMissing = {};
1946
- writeConfig(configMissing);
1947
-
1948
- const mockRun = jest
1949
- .fn()
1950
- .mockResolvedValue({ reviewReady: true, rejections: [] });
1951
- const MockedCheckUseCase = jest.mocked(CheckIssueReviewReadinessUseCase);
1952
-
1953
- MockedCheckUseCase.mockImplementation(function (
1954
- this: CheckIssueReviewReadinessUseCase,
1955
- ) {
1956
- this.run = mockRun;
1957
- return this;
1958
- });
1959
-
1960
- const stdoutSpy = jest
1961
- .spyOn(process.stdout, 'write')
1962
- .mockImplementation(() => true);
1963
-
1964
- await program.parseAsync([
1965
- 'node',
1966
- 'test',
1967
- 'checkIssueReviewReadiness',
1968
- '--configFilePath',
1969
- configFilePath,
1970
- '--issueUrl',
1971
- 'https://github.com/test/repo/issues/1',
1972
- ]);
1973
-
1974
- expect(mockRun).toHaveBeenCalledTimes(1);
1975
- expect(mockRun).toHaveBeenCalledWith({
1976
- issueUrl: 'https://github.com/test/repo/issues/1',
1977
- allowedIssueAuthors: null,
1978
- labelsAsLlmAgentName: null,
1979
- labelsNotRequiringPullRequest: null,
1980
- developerAgentNames: null,
1981
- });
1982
-
1983
- stdoutSpy.mockRestore();
1984
- });
1985
-
1986
- it('should exit with error when GH_TOKEN is missing', async () => {
1987
- delete process.env.GH_TOKEN;
1988
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
1989
- const processExitSpy = jest
1990
- .spyOn(process, 'exit')
1991
- .mockImplementation(() => {
1992
- throw new Error('process.exit called');
1993
- });
1994
-
1995
- await expect(
1996
- program.parseAsync([
1997
- 'node',
1998
- 'test',
1999
- 'checkIssueReviewReadiness',
2000
- '--configFilePath',
2001
- configFilePath,
2002
- '--issueUrl',
2003
- 'https://github.com/test/repo/issues/1',
2004
- ]),
2005
- ).rejects.toThrow('process.exit called');
2006
-
2007
- expect(consoleErrorSpy).toHaveBeenCalledWith(
2008
- 'GH_TOKEN environment variable is required',
2009
- );
2010
- expect(processExitSpy).toHaveBeenCalledWith(1);
2011
-
2012
- consoleErrorSpy.mockRestore();
2013
- processExitSpy.mockRestore();
2014
- });
2015
- });
2016
-
2017
- describe('serveWeb', () => {
2018
- it('should appear in the CLI help output', () => {
2019
- const helpText = program.helpInformation();
2020
- expect(helpText).toContain('serveWeb');
2021
- });
2022
-
2023
- it('should start the server on the default port 9980 when --port is omitted', async () => {
2024
- writeConfig({ ...defaultConfig, consoleAccessToken: 'config-token' });
2025
- const logSpy = jest.spyOn(console, 'log').mockImplementation();
2026
-
2027
- await program.parseAsync([
2028
- 'node',
2029
- 'test',
2030
- 'serveWeb',
2031
- '--configFilePath',
2032
- configFilePath,
2033
- '--dashboardProjectNames',
2034
- 'acme,globex',
2035
- ]);
2036
-
2037
- expect(mockStartWebServer).toHaveBeenCalledTimes(1);
2038
- const callArg = mockStartWebServer.mock.calls[0][0];
2039
- expect(callArg.port).toBe(9980);
2040
- expect(callArg.accessToken).toBe('config-token');
2041
- expect(callArg.consoleDataOutputDir).toBeNull();
2042
- expect(callArg.dashboardProjectNames).toEqual(['acme', 'globex']);
2043
- expect(callArg.uiDistDir).toBe(
2044
- path.join(__dirname, '..', 'console', 'ui-dist'),
2045
- );
2046
- expect(fs.existsSync(path.join(callArg.uiDistDir, 'index.html'))).toBe(
2047
- true,
2048
- );
2049
-
2050
- logSpy.mockRestore();
2051
- });
2052
-
2053
- it('should exit with error when --dashboardProjectNames is omitted', async () => {
2054
- writeConfig({ ...defaultConfig, consoleAccessToken: 'config-token' });
2055
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
2056
- const processExitSpy = jest
2057
- .spyOn(process, 'exit')
2058
- .mockImplementation(() => {
2059
- throw new Error('process.exit called');
2060
- });
2061
-
2062
- await expect(
2063
- program.parseAsync([
2064
- 'node',
2065
- 'test',
2066
- 'serveWeb',
2067
- '--configFilePath',
2068
- configFilePath,
2069
- ]),
2070
- ).rejects.toThrow('process.exit called');
2071
-
2072
- expect(consoleErrorSpy).toHaveBeenCalledWith(
2073
- '--dashboardProjectNames must list at least one project name',
2074
- );
2075
- expect(processExitSpy).toHaveBeenCalledWith(1);
2076
- expect(mockStartWebServer).not.toHaveBeenCalled();
2077
-
2078
- consoleErrorSpy.mockRestore();
2079
- processExitSpy.mockRestore();
2080
- });
2081
-
2082
- it('should exit with error when two --dashboardProjectNames share a display label', async () => {
2083
- writeConfig({ ...defaultConfig, consoleAccessToken: 'config-token' });
2084
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
2085
- const processExitSpy = jest
2086
- .spyOn(process, 'exit')
2087
- .mockImplementation(() => {
2088
- throw new Error('process.exit called');
2089
- });
2090
-
2091
- await expect(
2092
- program.parseAsync([
2093
- 'node',
2094
- 'test',
2095
- 'serveWeb',
2096
- '--configFilePath',
2097
- configFilePath,
2098
- '--dashboardProjectNames',
2099
- 'acme,acmelabs',
2100
- ]),
2101
- ).rejects.toThrow('process.exit called');
2102
-
2103
- expect(consoleErrorSpy).toHaveBeenCalledWith(
2104
- 'Dashboard project names acme and acmelabs share the display label ac',
2105
- );
2106
- expect(processExitSpy).toHaveBeenCalledWith(1);
2107
- expect(mockStartWebServer).not.toHaveBeenCalled();
2108
-
2109
- consoleErrorSpy.mockRestore();
2110
- processExitSpy.mockRestore();
2111
- });
2112
-
2113
- it('should validate --dashboardProjectNames before constructing the GitHub repositories', async () => {
2114
- writeConfig({ ...defaultConfig, consoleAccessToken: 'config-token' });
2115
- jest.mocked(GraphqlProjectRepository).mockClear();
2116
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
2117
- const processExitSpy = jest
2118
- .spyOn(process, 'exit')
2119
- .mockImplementation(() => {
2120
- throw new Error('process.exit called');
2121
- });
2122
-
2123
- await expect(
2124
- program.parseAsync([
2125
- 'node',
2126
- 'test',
2127
- 'serveWeb',
2128
- '--configFilePath',
2129
- configFilePath,
2130
- ]),
2131
- ).rejects.toThrow('process.exit called');
2132
-
2133
- expect(jest.mocked(GraphqlProjectRepository)).not.toHaveBeenCalled();
2134
-
2135
- consoleErrorSpy.mockRestore();
2136
- processExitSpy.mockRestore();
2137
- });
2138
-
2139
- it('should use the provided --port and --consoleDataOutputDir', async () => {
2140
- writeConfig({ ...defaultConfig, consoleAccessToken: 'config-token' });
2141
- const logSpy = jest.spyOn(console, 'log').mockImplementation();
2142
-
2143
- await program.parseAsync([
2144
- 'node',
2145
- 'test',
2146
- 'serveWeb',
2147
- '--configFilePath',
2148
- configFilePath,
2149
- '--port',
2150
- '12345',
2151
- '--consoleDataOutputDir',
2152
- '/tmp/console-data',
2153
- '--dashboardProjectNames',
2154
- 'acme,globex',
2155
- ]);
2156
-
2157
- const callArg = mockStartWebServer.mock.calls[0][0];
2158
- expect(callArg.port).toBe(12345);
2159
- expect(callArg.consoleDataOutputDir).toBe('/tmp/console-data');
2160
-
2161
- logSpy.mockRestore();
2162
- });
2163
-
2164
- it('should exit with error when consoleAccessToken is missing from config', async () => {
2165
- writeConfig(defaultConfig);
2166
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
2167
- const processExitSpy = jest
2168
- .spyOn(process, 'exit')
2169
- .mockImplementation(() => {
2170
- throw new Error('process.exit called');
2171
- });
2172
-
2173
- await expect(
2174
- program.parseAsync([
2175
- 'node',
2176
- 'test',
2177
- 'serveWeb',
2178
- '--configFilePath',
2179
- configFilePath,
2180
- ]),
2181
- ).rejects.toThrow('process.exit called');
2182
-
2183
- expect(consoleErrorSpy).toHaveBeenCalledWith(
2184
- 'consoleAccessToken is required. Provide it via the config file.',
2185
- );
2186
- expect(processExitSpy).toHaveBeenCalledWith(1);
2187
-
2188
- consoleErrorSpy.mockRestore();
2189
- processExitSpy.mockRestore();
2190
- });
2191
-
2192
- it('should exit with error for an invalid --port value', async () => {
2193
- writeConfig({ ...defaultConfig, consoleAccessToken: 'config-token' });
2194
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
2195
- const processExitSpy = jest
2196
- .spyOn(process, 'exit')
2197
- .mockImplementation(() => {
2198
- throw new Error('process.exit called');
2199
- });
2200
-
2201
- await expect(
2202
- program.parseAsync([
2203
- 'node',
2204
- 'test',
2205
- 'serveWeb',
2206
- '--configFilePath',
2207
- configFilePath,
2208
- '--port',
2209
- 'not-a-number',
2210
- ]),
2211
- ).rejects.toThrow('process.exit called');
2212
-
2213
- expect(processExitSpy).toHaveBeenCalledWith(1);
2214
-
2215
- consoleErrorSpy.mockRestore();
2216
- processExitSpy.mockRestore();
2217
- });
2218
-
2219
- it('should inject the resolved project and issue repository into the server', async () => {
2220
- writeConfig({ ...defaultConfig, consoleAccessToken: 'config-token' });
2221
- const logSpy = jest.spyOn(console, 'log').mockImplementation();
2222
-
2223
- await program.parseAsync([
2224
- 'node',
2225
- 'test',
2226
- 'serveWeb',
2227
- '--configFilePath',
2228
- configFilePath,
2229
- '--dashboardProjectNames',
2230
- 'acme,globex',
2231
- ]);
2232
-
2233
- const callArg = mockStartWebServer.mock.calls[0][0];
2234
- expect(callArg.issueRepository).not.toBeNull();
2235
- expect(callArg.issueTitleStateCache).not.toBeNull();
2236
-
2237
- const resolveProject = callArg.resolveProject;
2238
- if (resolveProject === null || resolveProject === undefined) {
2239
- throw new Error('resolveProject was not injected');
2240
- }
2241
- const binding = await resolveProject('test-project');
2242
- expect(binding).not.toBeNull();
2243
- expect(binding?.pjcode).toBe('test-project');
2244
- expect(binding?.project.name).toBe('test-project');
2245
-
2246
- const unknownBinding = await resolveProject('not-configured');
2247
- expect(unknownBinding).toBeNull();
2248
-
2249
- logSpy.mockRestore();
2250
- });
2251
-
2252
- it('should exit with error when GH_TOKEN is missing', async () => {
2253
- delete process.env.GH_TOKEN;
2254
- writeConfig({ ...defaultConfig, consoleAccessToken: 'config-token' });
2255
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
2256
- const processExitSpy = jest
2257
- .spyOn(process, 'exit')
2258
- .mockImplementation(() => {
2259
- throw new Error('process.exit called');
2260
- });
2261
-
2262
- await expect(
2263
- program.parseAsync([
2264
- 'node',
2265
- 'test',
2266
- 'serveWeb',
2267
- '--configFilePath',
2268
- configFilePath,
2269
- ]),
2270
- ).rejects.toThrow('process.exit called');
2271
-
2272
- expect(consoleErrorSpy).toHaveBeenCalledWith(
2273
- 'GH_TOKEN environment variable is required',
2274
- );
2275
-
2276
- consoleErrorSpy.mockRestore();
2277
- processExitSpy.mockRestore();
2278
- });
2279
-
2280
- it('should exit with error when projectUrl is missing from config', async () => {
2281
- writeConfig({
2282
- defaultAgentName: 'agent1',
2283
- projectName: 'test-project',
2284
- consoleAccessToken: 'config-token',
2285
- });
2286
- mockFetchReturningReadme(null);
2287
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
2288
- const processExitSpy = jest
2289
- .spyOn(process, 'exit')
2290
- .mockImplementation(() => {
2291
- throw new Error('process.exit called');
2292
- });
2293
-
2294
- await expect(
2295
- program.parseAsync([
2296
- 'node',
2297
- 'test',
2298
- 'serveWeb',
2299
- '--configFilePath',
2300
- configFilePath,
2301
- ]),
2302
- ).rejects.toThrow('process.exit called');
2303
-
2304
- expect(consoleErrorSpy).toHaveBeenCalledWith(
2305
- 'projectUrl is required. Provide it via the config file or project README.',
2306
- );
2307
-
2308
- consoleErrorSpy.mockRestore();
2309
- processExitSpy.mockRestore();
2310
- });
2311
- });
2312
-
2313
- describe('serveConsole (deprecated alias)', () => {
2314
- it('should appear in the CLI help output', () => {
2315
- const helpText = program.helpInformation();
2316
- expect(helpText).toContain('serveConsole');
2317
- });
2318
-
2319
- it('should route to the same handler as serveWeb on the default port 9980', async () => {
2320
- writeConfig({ ...defaultConfig, consoleAccessToken: 'config-token' });
2321
- const logSpy = jest.spyOn(console, 'log').mockImplementation();
2322
-
2323
- await program.parseAsync([
2324
- 'node',
2325
- 'test',
2326
- 'serveConsole',
2327
- '--configFilePath',
2328
- configFilePath,
2329
- '--dashboardProjectNames',
2330
- 'acme,globex',
2331
- ]);
2332
-
2333
- expect(mockStartWebServer).toHaveBeenCalledTimes(1);
2334
- const callArg = mockStartWebServer.mock.calls[0][0];
2335
- expect(callArg.port).toBe(9980);
2336
- expect(callArg.accessToken).toBe('config-token');
2337
- expect(callArg.consoleDataOutputDir).toBeNull();
2338
- expect(callArg.uiDistDir).toBe(
2339
- path.join(__dirname, '..', 'console', 'ui-dist'),
2340
- );
2341
-
2342
- logSpy.mockRestore();
2343
- });
2344
-
2345
- it('should use the provided --port and --consoleDataOutputDir', async () => {
2346
- writeConfig({ ...defaultConfig, consoleAccessToken: 'config-token' });
2347
- const logSpy = jest.spyOn(console, 'log').mockImplementation();
2348
-
2349
- await program.parseAsync([
2350
- 'node',
2351
- 'test',
2352
- 'serveConsole',
2353
- '--configFilePath',
2354
- configFilePath,
2355
- '--port',
2356
- '12345',
2357
- '--consoleDataOutputDir',
2358
- '/tmp/console-data',
2359
- '--dashboardProjectNames',
2360
- 'acme,globex',
2361
- ]);
2362
-
2363
- const callArg = mockStartWebServer.mock.calls[0][0];
2364
- expect(callArg.port).toBe(12345);
2365
- expect(callArg.consoleDataOutputDir).toBe('/tmp/console-data');
2366
-
2367
- logSpy.mockRestore();
2368
- });
2369
- });
2370
-
2371
- describe('killTmuxSession', () => {
2372
- it('reaches the real repository command sequence to kill a named session via --session', async () => {
2373
- await program.parseAsync([
2374
- 'node',
2375
- 'test',
2376
- 'killTmuxSession',
2377
- '--session',
2378
- 'https_//github_com/owner/repo/issues/9',
2379
- ]);
2380
-
2381
- const expectedScopeUnitName =
2382
- 'cl-https---github-com-owner-repo-issues-9.scope';
2383
- expect(mockRunCommand.mock.calls).toEqual([
2384
- ['systemctl', ['--user', 'reset-failed', expectedScopeUnitName]],
2385
- ['systemctl', ['--user', 'stop', expectedScopeUnitName]],
2386
- ['systemctl', ['--user', 'reset-failed', expectedScopeUnitName]],
2387
- [
2388
- 'tmux',
2389
- ['kill-session', '-t', '=https_//github_com/owner/repo/issues/9'],
2390
- ],
2391
- ]);
2392
- });
2393
-
2394
- it('reaches the real repository command sequence to stop its own scope via --self, without calling tmux', async () => {
2395
- const actualFs = jest.requireActual<typeof fs>('fs');
2396
- const readFileSyncMock = jest.mocked(fs.readFileSync);
2397
- readFileSyncMock.mockImplementation(
2398
- (
2399
- filePath: Parameters<typeof fs.readFileSync>[0],
2400
- options: Parameters<typeof fs.readFileSync>[1],
2401
- ): ReturnType<typeof fs.readFileSync> => {
2402
- if (filePath === '/proc/self/cgroup') {
2403
- return '0::/user.slice/user-1000.slice/user@1000.service/app.slice/cl-current-session.scope\n';
2404
- }
2405
- return actualFs.readFileSync(filePath, options);
2406
- },
2407
- );
2408
-
2409
- await program.parseAsync(['node', 'test', 'killTmuxSession', '--self']);
2410
-
2411
- expect(mockRunCommand.mock.calls).toEqual([
2412
- ['systemctl', ['--user', 'reset-failed', 'cl-current-session.scope']],
2413
- ['systemctl', ['--user', 'stop', 'cl-current-session.scope']],
2414
- ['systemctl', ['--user', 'reset-failed', 'cl-current-session.scope']],
2415
- ]);
2416
- expect(mockRunCommand.mock.calls.some((call) => call[0] === 'tmux')).toBe(
2417
- false,
2418
- );
2419
-
2420
- readFileSyncMock.mockImplementation(actualFs.readFileSync);
2421
- });
2422
-
2423
- it('exits with an error when neither --session nor --self is provided', async () => {
2424
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
2425
- const processExitSpy = jest
2426
- .spyOn(process, 'exit')
2427
- .mockImplementation(() => {
2428
- throw new Error('process.exit called');
2429
- });
2430
-
2431
- await expect(
2432
- program.parseAsync(['node', 'test', 'killTmuxSession']),
2433
- ).rejects.toThrow('process.exit called');
2434
-
2435
- expect(consoleErrorSpy).toHaveBeenCalledWith(
2436
- 'Either --session <name> or --self is required',
2437
- );
2438
- expect(processExitSpy).toHaveBeenCalledWith(1);
2439
-
2440
- consoleErrorSpy.mockRestore();
2441
- processExitSpy.mockRestore();
2442
- });
2443
-
2444
- it('exits with an error when both --session and --self are provided', async () => {
2445
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
2446
- const processExitSpy = jest
2447
- .spyOn(process, 'exit')
2448
- .mockImplementation(() => {
2449
- throw new Error('process.exit called');
2450
- });
2451
-
2452
- await expect(
2453
- program.parseAsync([
2454
- 'node',
2455
- 'test',
2456
- 'killTmuxSession',
2457
- '--session',
2458
- 'some_session',
2459
- '--self',
2460
- ]),
2461
- ).rejects.toThrow('process.exit called');
2462
-
2463
- expect(consoleErrorSpy).toHaveBeenCalledWith(
2464
- '--session and --self cannot be used together',
2465
- );
2466
- expect(processExitSpy).toHaveBeenCalledWith(1);
2467
-
2468
- consoleErrorSpy.mockRestore();
2469
- processExitSpy.mockRestore();
2470
- });
2471
- });
2472
-
2473
- describe('attachOrCreate', () => {
2474
- const issueUrl = 'https://github.com/owner/repo/issues/9';
2475
- let scopeLibPath = '';
2476
-
2477
- beforeEach(() => {
2478
- scopeLibPath = path.join(
2479
- os.tmpdir(),
2480
- `test-cl-scope-lib-${Date.now()}.sh`,
2481
- );
2482
- fs.writeFileSync(scopeLibPath, '#!/bin/bash\n');
2483
- process.env.CL_SCOPE_LIB_PATH = scopeLibPath;
2484
- });
2485
-
2486
- afterEach(() => {
2487
- if (fs.existsSync(scopeLibPath)) {
2488
- fs.unlinkSync(scopeLibPath);
2489
- }
2490
- delete process.env.CL_SCOPE_LIB_PATH;
2491
- });
2492
-
2493
- it('attaches to the registered session when registry returns a live session', async () => {
2494
- mockRunCommand
2495
- .mockResolvedValueOnce({ stdout: 'wq7x3mk\n', stderr: '', exitCode: 0 })
2496
- .mockResolvedValueOnce({ stdout: '', stderr: '', exitCode: 0 });
2497
-
2498
- await program.parseAsync([
2499
- 'node',
2500
- 'test',
2501
- 'attachOrCreate',
2502
- '--issueUrl',
2503
- issueUrl,
2504
- ]);
2505
-
2506
- expect(mockRunCommand.mock.calls[0]).toEqual([
2507
- 'bash',
2508
- [scopeLibPath, 'registry-get-session', issueUrl],
2509
- ]);
2510
- expect(mockRunCommand.mock.calls[1]).toEqual([
2511
- 'tmux',
2512
- ['has-session', '-t', '=wq7x3mk'],
2513
- ]);
2514
- expect(mockSpawnInteractive).toHaveBeenCalledWith('tmux', [
2515
- 'attach-session',
2516
- '-t',
2517
- '=wq7x3mk',
2518
- ]);
2519
- });
2520
-
2521
- it('creates a new session when no registered session is found', async () => {
2522
- mockRunCommand.mockResolvedValueOnce({
2523
- stdout: '',
2524
- stderr: '',
2525
- exitCode: 0,
2526
- });
2527
-
2528
- await program.parseAsync([
2529
- 'node',
2530
- 'test',
2531
- 'attachOrCreate',
2532
- '--issueUrl',
2533
- issueUrl,
2534
- ]);
2535
-
2536
- expect(mockSpawnInteractive).toHaveBeenCalledWith('tmux', [
2537
- 'new-session',
2538
- '-A',
2539
- '-s',
2540
- issueUrl,
2541
- 'cl',
2542
- issueUrl,
2543
- ]);
2544
- });
2545
-
2546
- it('creates a new session when no scope lib path is available', async () => {
2547
- jest
2548
- .mocked(fs.existsSync)
2549
- .mockReturnValueOnce(false)
2550
- .mockReturnValueOnce(false);
2551
-
2552
- await program.parseAsync([
2553
- 'node',
2554
- 'test',
2555
- 'attachOrCreate',
2556
- '--issueUrl',
2557
- issueUrl,
2558
- ]);
2559
-
2560
- expect(mockRunCommand.mock.calls).toHaveLength(0);
2561
- expect(mockSpawnInteractive).toHaveBeenCalledWith('tmux', [
2562
- 'new-session',
2563
- '-A',
2564
- '-s',
2565
- issueUrl,
2566
- 'cl',
2567
- issueUrl,
2568
- ]);
2569
- });
2570
- });
2571
-
2572
- describe('ownerCallFileAppend and ownerCallFileDelete', () => {
2573
- const issueUrl = 'https://github.com/OWNER/REPO/issues/1';
2574
- const otherIssueUrl = 'https://github.com/OWNER/REPO/issues/2';
2575
- const sessionName = toTmuxSessionName(issueUrl);
2576
- let ownerCallDataDir = '';
2577
- let bodyFilePath = '';
2578
-
2579
- beforeEach(() => {
2580
- ownerCallDataDir = fs.mkdtempSync(
2581
- path.join(os.tmpdir(), 'owner-call-cli-'),
2582
- );
2583
- bodyFilePath = path.join(ownerCallDataDir, 'body.txt');
2584
- fs.writeFileSync(bodyFilePath, ' the call body\n\nsecond paragraph\n');
2585
- });
2586
-
2587
- afterEach(() => {
2588
- fs.rmSync(ownerCallDataDir, { recursive: true, force: true });
2589
- });
2590
-
2591
- // The in-tmux-by-human data the scheduled run writes into the same
2592
- // directory serveWeb serves: one file per project, each listing the
2593
- // sessions of that project by their raw issue url.
2594
- type InTmuxByHumanProjectData = {
2595
- projectCode: string;
2596
- sessionIssueUrls: string[];
2597
- };
2598
-
2599
- const writeInTmuxByHumanData = (
2600
- projects: InTmuxByHumanProjectData[],
2601
- ): void => {
2602
- for (const project of projects) {
2603
- fs.writeFileSync(
2604
- path.join(ownerCallDataDir, `${project.projectCode}.v4.json`),
2605
- `${JSON.stringify({
2606
- version: 4,
2607
- overviewUrl: 'https://github.com/orgs/OWNER/projects/1',
2608
- tdpmConsoleUrl: 'http://localhost/projects/code',
2609
- newIssueUrl: 'https://github.com/OWNER/REPO/issues/new',
2610
- groups: [
2611
- {
2612
- story: 'a story',
2613
- sessions: project.sessionIssueUrls.map((url) => ({
2614
- name: url,
2615
- description: 'an issue title',
2616
- })),
2617
- },
2618
- ],
2619
- })}\n`,
2620
- );
2621
- }
2622
- };
2623
-
2624
- const projectCodeListing = (
2625
- projects: InTmuxByHumanProjectData[],
2626
- url: string,
2627
- ): string | null =>
2628
- projects.find((project) => project.sessionIssueUrls.includes(url))
2629
- ?.projectCode ?? null;
2630
-
2631
- const appendedFilePath = (projectCode: string | null): string =>
2632
- path.join(
2633
- ownerCallDataDir,
2634
- ownerCallFileRelativePath(projectCode, sessionName),
2635
- );
2636
-
2637
- const runAppend = (calledAt: string): Promise<unknown> =>
2638
- program.parseAsync([
2639
- 'node',
2640
- 'test',
2641
- 'ownerCallFileAppend',
2642
- '--session',
2643
- sessionName,
2644
- '--calledAt',
2645
- calledAt,
2646
- '--body-file',
2647
- bodyFilePath,
2648
- '--inTmuxDataDir',
2649
- ownerCallDataDir,
2650
- ]);
2651
-
2652
- it('appends the call under the directory of the project whose session list holds the session', async () => {
2653
- const projects: InTmuxByHumanProjectData[] = [
2654
- { projectCode: 'other', sessionIssueUrls: [otherIssueUrl] },
2655
- { projectCode: 'umino', sessionIssueUrls: [issueUrl] },
2656
- ];
2657
- writeInTmuxByHumanData(projects);
2658
- const stdoutSpy = jest
2659
- .spyOn(process.stdout, 'write')
2660
- .mockImplementation(() => true);
2661
-
2662
- await runAppend('2026-08-14T04:22:28Z');
2663
-
2664
- const documents = YAML.parseAllDocuments(
2665
- fs.readFileSync(
2666
- appendedFilePath(projectCodeListing(projects, issueUrl)),
2667
- 'utf-8',
2668
- ),
2669
- );
2670
- expect(documents).toHaveLength(1);
2671
- expect(documents[0].toJS()).toEqual({
2672
- sessionName,
2673
- calledAt: '2026-08-14T04:22:28Z',
2674
- body: ' the call body\n\nsecond paragraph\n',
2675
- });
2676
- expect(stdoutSpy).not.toHaveBeenCalled();
2677
-
2678
- stdoutSpy.mockRestore();
2679
- });
2680
-
2681
- it('appends the call under NA when no project lists the session', async () => {
2682
- const projects: InTmuxByHumanProjectData[] = [
2683
- { projectCode: 'umino', sessionIssueUrls: [otherIssueUrl] },
2684
- ];
2685
- writeInTmuxByHumanData(projects);
2686
-
2687
- await runAppend('2026-08-14T04:22:28Z');
2688
-
2689
- expect(projectCodeListing(projects, issueUrl)).toBeNull();
2690
- expect(fs.existsSync(appendedFilePath(null))).toBe(true);
2691
- });
2692
-
2693
- it('rejects a calledAt that is not a UTC ISO-8601 timestamp with second precision', async () => {
2694
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
2695
- const processExitSpy = jest
2696
- .spyOn(process, 'exit')
2697
- .mockImplementation(() => {
2698
- throw new Error('process.exit called');
2699
- });
2700
-
2701
- await expect(runAppend('2026-08-14 04:22:28')).rejects.toThrow(
2702
- 'process.exit called',
2703
- );
2704
- expect(processExitSpy).toHaveBeenCalledWith(1);
2705
- expect(fs.existsSync(appendedFilePath(null))).toBe(false);
2706
-
2707
- consoleErrorSpy.mockRestore();
2708
- processExitSpy.mockRestore();
2709
- });
2710
-
2711
- const runDelete = (): Promise<unknown> =>
2712
- program.parseAsync([
2713
- 'node',
2714
- 'test',
2715
- 'ownerCallFileDelete',
2716
- '--session',
2717
- sessionName,
2718
- '--inTmuxDataDir',
2719
- ownerCallDataDir,
2720
- ]);
2721
-
2722
- it('deletes the file of the session whichever project directory holds it', async () => {
2723
- const projects: InTmuxByHumanProjectData[] = [
2724
- { projectCode: 'umino', sessionIssueUrls: [issueUrl] },
2725
- ];
2726
- writeInTmuxByHumanData(projects);
2727
- await runAppend('2026-08-14T04:22:28Z');
2728
- const writtenFilePath = appendedFilePath(
2729
- projectCodeListing(projects, issueUrl),
2730
- );
2731
- expect(fs.existsSync(writtenFilePath)).toBe(true);
2732
-
2733
- await runDelete();
2734
-
2735
- expect(fs.existsSync(writtenFilePath)).toBe(false);
2736
- });
2737
-
2738
- it('deletes the file left behind under the project the session has since left', async () => {
2739
- writeInTmuxByHumanData([
2740
- { projectCode: 'umino', sessionIssueUrls: [issueUrl] },
2741
- ]);
2742
- await runAppend('2026-08-14T04:22:28Z');
2743
- writeInTmuxByHumanData([
2744
- { projectCode: 'umino', sessionIssueUrls: [] },
2745
- { projectCode: 'other', sessionIssueUrls: [issueUrl] },
2746
- ]);
2747
-
2748
- await runDelete();
2749
-
2750
- expect(fs.existsSync(appendedFilePath('umino'))).toBe(false);
2751
- });
2752
-
2753
- it('succeeds when the file to delete is already absent', async () => {
2754
- await expect(runDelete()).resolves.toBeDefined();
2755
- });
2756
- });
2757
-
2758
- describe('runCliProgram', () => {
2759
- const mockCheckUseCaseRejectingWith = (error: Error): void => {
2760
- const MockedCheckUseCase = jest.mocked(CheckIssueReviewReadinessUseCase);
2761
- MockedCheckUseCase.mockImplementation(function (
2762
- this: CheckIssueReviewReadinessUseCase,
2763
- ) {
2764
- this.run = jest.fn().mockRejectedValue(error);
2765
- return this;
2766
- });
2767
- };
2768
-
2769
- const checkIssueReviewReadinessArgv = [
2770
- 'node',
2771
- 'test',
2772
- 'checkIssueReviewReadiness',
2773
- '--configFilePath',
2774
- configFilePath,
2775
- '--issueUrl',
2776
- 'https://github.com/test/repo/issues/1',
2777
- ];
2778
-
2779
- it('should pass the rejection of an async action handler to the fatal error handler', async () => {
2780
- const thrownError = new Error('GitHub API failure');
2781
- mockCheckUseCaseRejectingWith(thrownError);
2782
- const handleFatalError = jest.fn();
2783
-
2784
- await runCliProgram(checkIssueReviewReadinessArgv, handleFatalError);
2785
-
2786
- expect(handleFatalError).toHaveBeenCalledTimes(1);
2787
- expect(handleFatalError).toHaveBeenCalledWith(thrownError);
2788
- });
2789
-
2790
- it('should not call the fatal error handler when the async action handler succeeds', async () => {
2791
- const mockRun = jest
2792
- .fn()
2793
- .mockResolvedValue({ reviewReady: true, rejections: [] });
2794
- const MockedCheckUseCase = jest.mocked(CheckIssueReviewReadinessUseCase);
2795
-
2796
- MockedCheckUseCase.mockImplementation(function (
2797
- this: CheckIssueReviewReadinessUseCase,
2798
- ) {
2799
- this.run = mockRun;
2800
- return this;
2801
- });
2802
- const stdoutSpy = jest
2803
- .spyOn(process.stdout, 'write')
2804
- .mockImplementation(() => true);
2805
- const handleFatalError = jest.fn();
2806
-
2807
- await runCliProgram(checkIssueReviewReadinessArgv, handleFatalError);
2808
-
2809
- expect(mockRun).toHaveBeenCalledTimes(1);
2810
- expect(handleFatalError).not.toHaveBeenCalled();
2811
-
2812
- stdoutSpy.mockRestore();
2813
- });
2814
- });
2815
-
2816
- describe('reportFatalErrorAndExit', () => {
2817
- it('should write the error to stderr and exit with code 1', () => {
2818
- const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
2819
- const processExitSpy = jest
2820
- .spyOn(process, 'exit')
2821
- .mockImplementation(() => {
2822
- throw new Error('process.exit called');
2823
- });
2824
- const thrownError = new Error('GitHub API failure');
2825
-
2826
- expect(() => reportFatalErrorAndExit(thrownError)).toThrow(
2827
- 'process.exit called',
2828
- );
2829
-
2830
- expect(consoleErrorSpy).toHaveBeenCalledWith(thrownError);
2831
- expect(processExitSpy).toHaveBeenCalledWith(1);
2832
-
2833
- consoleErrorSpy.mockRestore();
2834
- processExitSpy.mockRestore();
2835
- });
2836
- });
2837
- });