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

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