github-issue-tower-defence-management 1.60.2 → 1.63.1

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 (117) hide show
  1. package/.github/workflows/publish.yml +13 -0
  2. package/.github/workflows/test.yml +0 -4
  3. package/CHANGELOG.md +7 -0
  4. package/README.md +53 -10
  5. package/bin/adapter/entry-points/cli/index.js +11 -11
  6. package/bin/adapter/entry-points/cli/index.js.map +1 -1
  7. package/bin/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.js +3 -22
  8. package/bin/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.js.map +1 -1
  9. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +8 -22
  10. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  11. package/bin/adapter/entry-points/handlers/rotationOrderFileWriter.js +56 -0
  12. package/bin/adapter/entry-points/handlers/rotationOrderFileWriter.js.map +1 -0
  13. package/bin/adapter/entry-points/handlers/situationFileWriter.js +5 -0
  14. package/bin/adapter/entry-points/handlers/situationFileWriter.js.map +1 -1
  15. package/bin/adapter/proxy/TokenListLoader.js +21 -6
  16. package/bin/adapter/proxy/TokenListLoader.js.map +1 -1
  17. package/bin/adapter/proxy/proxyEntry.js +1 -0
  18. package/bin/adapter/proxy/proxyEntry.js.map +1 -1
  19. package/bin/adapter/repositories/BaseGitHubRepository.js +1 -113
  20. package/bin/adapter/repositories/BaseGitHubRepository.js.map +1 -1
  21. package/bin/adapter/repositories/ProxyClaudeTokenUsageRepository.js +5 -3
  22. package/bin/adapter/repositories/ProxyClaudeTokenUsageRepository.js.map +1 -1
  23. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +8 -7
  24. package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
  25. package/bin/domain/usecases/HandleScheduledEventUseCase.js +14 -3
  26. package/bin/domain/usecases/HandleScheduledEventUseCase.js.map +1 -1
  27. package/bin/domain/usecases/IssueRejectionEvaluator.js +8 -1
  28. package/bin/domain/usecases/IssueRejectionEvaluator.js.map +1 -1
  29. package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +5 -1
  30. package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -1
  31. package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js +1 -1
  32. package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js.map +1 -1
  33. package/bin/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.js +32 -1
  34. package/bin/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.js.map +1 -1
  35. package/bin/domain/usecases/StartPreparationUseCase.js +91 -12
  36. package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -1
  37. package/package.json +1 -4
  38. package/src/adapter/entry-points/cli/index.test.ts +16 -16
  39. package/src/adapter/entry-points/cli/index.ts +8 -11
  40. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.test.ts +2 -55
  41. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.ts +1 -11
  42. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.test.ts +6 -56
  43. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +7 -11
  44. package/src/adapter/entry-points/handlers/rotationOrderFileWriter.test.ts +177 -0
  45. package/src/adapter/entry-points/handlers/rotationOrderFileWriter.ts +20 -0
  46. package/src/adapter/entry-points/handlers/situationFileWriter.test.ts +36 -0
  47. package/src/adapter/entry-points/handlers/situationFileWriter.ts +8 -0
  48. package/src/adapter/proxy/TokenListLoader.test.ts +50 -1
  49. package/src/adapter/proxy/TokenListLoader.ts +25 -5
  50. package/src/adapter/proxy/proxyEntry.test.ts +270 -1
  51. package/src/adapter/proxy/proxyEntry.ts +2 -1
  52. package/src/adapter/repositories/BaseGitHubRepository.test.ts +1 -186
  53. package/src/adapter/repositories/BaseGitHubRepository.ts +1 -139
  54. package/src/adapter/repositories/GraphqlProjectRepository.errorHandling.test.ts +0 -1
  55. package/src/adapter/repositories/GraphqlProjectRepository.fetchProjectId.test.ts +4 -1
  56. package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.test.ts +60 -19
  57. package/src/adapter/repositories/ProxyClaudeTokenUsageRepository.ts +6 -4
  58. package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +23 -13
  59. package/src/adapter/repositories/issue/ApiV3IssueRepository.test.ts +0 -1
  60. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +0 -8
  61. package/src/adapter/repositories/issue/RestIssueRepository.test.ts +0 -1
  62. package/src/domain/entities/ClaudeTokenUsage.ts +1 -0
  63. package/src/domain/usecases/HandleScheduledEventUseCase.test.ts +4 -0
  64. package/src/domain/usecases/HandleScheduledEventUseCase.ts +20 -5
  65. package/src/domain/usecases/IssueRejectionEvaluator.test.ts +153 -0
  66. package/src/domain/usecases/IssueRejectionEvaluator.ts +8 -0
  67. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +175 -31
  68. package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +7 -1
  69. package/src/domain/usecases/RevertNotReadyAwaitingQualityCheckUseCase.test.ts +32 -0
  70. package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +39 -5
  71. package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +1 -1
  72. package/src/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.test.ts +139 -20
  73. package/src/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.ts +62 -2
  74. package/src/domain/usecases/StartPreparationUseCase.test.ts +404 -21
  75. package/src/domain/usecases/StartPreparationUseCase.ts +152 -16
  76. package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +16 -0
  77. package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
  78. package/types/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.d.ts.map +1 -1
  79. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
  80. package/types/adapter/entry-points/handlers/rotationOrderFileWriter.d.ts +3 -0
  81. package/types/adapter/entry-points/handlers/rotationOrderFileWriter.d.ts.map +1 -0
  82. package/types/adapter/entry-points/handlers/situationFileWriter.d.ts +1 -0
  83. package/types/adapter/entry-points/handlers/situationFileWriter.d.ts.map +1 -1
  84. package/types/adapter/proxy/TokenListLoader.d.ts +5 -0
  85. package/types/adapter/proxy/TokenListLoader.d.ts.map +1 -1
  86. package/types/adapter/proxy/proxyEntry.d.ts +2 -1
  87. package/types/adapter/proxy/proxyEntry.d.ts.map +1 -1
  88. package/types/adapter/repositories/BaseGitHubRepository.d.ts +1 -23
  89. package/types/adapter/repositories/BaseGitHubRepository.d.ts.map +1 -1
  90. package/types/adapter/repositories/ProxyClaudeTokenUsageRepository.d.ts.map +1 -1
  91. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +14 -5
  92. package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
  93. package/types/domain/entities/ClaudeTokenUsage.d.ts +1 -0
  94. package/types/domain/entities/ClaudeTokenUsage.d.ts.map +1 -1
  95. package/types/domain/usecases/HandleScheduledEventUseCase.d.ts +5 -2
  96. package/types/domain/usecases/HandleScheduledEventUseCase.d.ts.map +1 -1
  97. package/types/domain/usecases/IssueRejectionEvaluator.d.ts +1 -1
  98. package/types/domain/usecases/IssueRejectionEvaluator.d.ts.map +1 -1
  99. package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts.map +1 -1
  100. package/types/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.d.ts +5 -2
  101. package/types/domain/usecases/SetWorkflowManagementIssueToStoryUseCase.d.ts.map +1 -1
  102. package/types/domain/usecases/StartPreparationUseCase.d.ts +15 -1
  103. package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -1
  104. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +14 -0
  105. package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
  106. package/bin/adapter/repositories/issue/CheerioIssueRepository.js +0 -136
  107. package/bin/adapter/repositories/issue/CheerioIssueRepository.js.map +0 -1
  108. package/bin/adapter/repositories/issue/InternalGraphqlIssueRepository.js +0 -1606
  109. package/bin/adapter/repositories/issue/InternalGraphqlIssueRepository.js.map +0 -1
  110. package/src/adapter/repositories/issue/CheerioIssueRepository.test.ts +0 -6552
  111. package/src/adapter/repositories/issue/CheerioIssueRepository.ts +0 -142
  112. package/src/adapter/repositories/issue/InternalGraphqlIssueRepository.test.ts +0 -118
  113. package/src/adapter/repositories/issue/InternalGraphqlIssueRepository.ts +0 -584
  114. package/types/adapter/repositories/issue/CheerioIssueRepository.d.ts +0 -40
  115. package/types/adapter/repositories/issue/CheerioIssueRepository.d.ts.map +0 -1
  116. package/types/adapter/repositories/issue/InternalGraphqlIssueRepository.d.ts +0 -220
  117. package/types/adapter/repositories/issue/InternalGraphqlIssueRepository.d.ts.map +0 -1
@@ -1,6 +1,6 @@
1
1
  const mockEnsureProxyRunning = jest.fn();
2
2
  const mockReadRateLimit = jest.fn();
3
- const mockLoadTokens = jest.fn();
3
+ const mockLoadTokenEntries = jest.fn();
4
4
 
5
5
  jest.mock('../proxy/ensureProxyRunning', () => ({
6
6
  ensureProxyRunning: mockEnsureProxyRunning,
@@ -12,7 +12,7 @@ jest.mock('../proxy/RateLimitCache', () => ({
12
12
  }));
13
13
 
14
14
  jest.mock('../proxy/TokenListLoader', () => ({
15
- loadTokens: mockLoadTokens,
15
+ loadTokenEntries: mockLoadTokenEntries,
16
16
  }));
17
17
 
18
18
  import { ProxyClaudeTokenUsageRepository } from './ProxyClaudeTokenUsageRepository';
@@ -52,24 +52,27 @@ describe('ProxyClaudeTokenUsageRepository', () => {
52
52
  const result = await repository.getAvailableTokenUsages();
53
53
 
54
54
  expect(result).toEqual([]);
55
- expect(mockLoadTokens.mock.calls).toHaveLength(0);
55
+ expect(mockLoadTokenEntries.mock.calls).toHaveLength(0);
56
56
  });
57
57
 
58
58
  it('should return an empty list when the token list cannot be loaded', async () => {
59
- mockLoadTokens.mockReturnValue(null);
59
+ mockLoadTokenEntries.mockReturnValue(null);
60
60
  const repository = new ProxyClaudeTokenUsageRepository('/tokens.json');
61
61
 
62
62
  const result = await repository.getAvailableTokenUsages();
63
63
 
64
64
  expect(result).toEqual([]);
65
- expect(mockLoadTokens.mock.calls).toEqual([['/tokens.json']]);
65
+ expect(mockLoadTokenEntries.mock.calls).toEqual([['/tokens.json']]);
66
66
  });
67
67
 
68
68
  const futureReset = Math.floor(Date.now() / 1000) + 3600;
69
69
  const pastReset = Math.floor(Date.now() / 1000) - 3600;
70
70
 
71
- it('should map each token to its cached utilization', async () => {
72
- mockLoadTokens.mockReturnValue(['token-a', 'token-b']);
71
+ it('should map each token to its cached utilization and include name', async () => {
72
+ mockLoadTokenEntries.mockReturnValue([
73
+ { name: 'alice', token: 'token-a' },
74
+ { name: 'bob', token: 'token-b' },
75
+ ]);
73
76
  mockReadRateLimit.mockImplementation((token: string) => {
74
77
  if (token === 'token-a') {
75
78
  return {
@@ -93,6 +96,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
93
96
 
94
97
  expect(result).toEqual([
95
98
  {
99
+ name: 'alice',
96
100
  token: 'token-a',
97
101
  fiveHourUtilization: 42,
98
102
  blocked: false,
@@ -100,6 +104,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
100
104
  modelWeeklyLimits: {},
101
105
  },
102
106
  {
107
+ name: 'bob',
103
108
  token: 'token-b',
104
109
  fiveHourUtilization: 0,
105
110
  blocked: false,
@@ -110,7 +115,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
110
115
  });
111
116
 
112
117
  it('should propagate the blocked status from the cache', async () => {
113
- mockLoadTokens.mockReturnValue(['token-a']);
118
+ mockLoadTokenEntries.mockReturnValue([
119
+ { name: 'alice', token: 'token-a' },
120
+ ]);
114
121
  mockReadRateLimit.mockReturnValue({
115
122
  fiveHourUtilization: 5,
116
123
  fiveHourReset: futureReset,
@@ -129,6 +136,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
129
136
 
130
137
  expect(result).toEqual([
131
138
  {
139
+ name: 'alice',
132
140
  token: 'token-a',
133
141
  fiveHourUtilization: 5,
134
142
  blocked: true,
@@ -139,7 +147,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
139
147
  });
140
148
 
141
149
  it('should propagate the rejected status from the cache', async () => {
142
- mockLoadTokens.mockReturnValue(['token-a']);
150
+ mockLoadTokenEntries.mockReturnValue([
151
+ { name: 'alice', token: 'token-a' },
152
+ ]);
143
153
  mockReadRateLimit.mockReturnValue({
144
154
  fiveHourUtilization: 100,
145
155
  fiveHourReset: futureReset,
@@ -158,6 +168,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
158
168
 
159
169
  expect(result).toEqual([
160
170
  {
171
+ name: 'alice',
161
172
  token: 'token-a',
162
173
  fiveHourUtilization: 100,
163
174
  blocked: false,
@@ -168,7 +179,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
168
179
  });
169
180
 
170
181
  it('should normalize fiveHourUtilization to 0 when the 5h reset has passed', async () => {
171
- mockLoadTokens.mockReturnValue(['token-a']);
182
+ mockLoadTokenEntries.mockReturnValue([
183
+ { name: 'alice', token: 'token-a' },
184
+ ]);
172
185
  mockReadRateLimit.mockReturnValue({
173
186
  fiveHourUtilization: 100,
174
187
  fiveHourReset: pastReset,
@@ -187,6 +200,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
187
200
 
188
201
  expect(result).toEqual([
189
202
  {
203
+ name: 'alice',
190
204
  token: 'token-a',
191
205
  fiveHourUtilization: 0,
192
206
  blocked: false,
@@ -197,7 +211,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
197
211
  });
198
212
 
199
213
  it('should keep fiveHourUtilization when the 5h reset is in the future', async () => {
200
- mockLoadTokens.mockReturnValue(['token-a']);
214
+ mockLoadTokenEntries.mockReturnValue([
215
+ { name: 'alice', token: 'token-a' },
216
+ ]);
201
217
  mockReadRateLimit.mockReturnValue({
202
218
  fiveHourUtilization: 95,
203
219
  fiveHourReset: futureReset,
@@ -216,6 +232,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
216
232
 
217
233
  expect(result).toEqual([
218
234
  {
235
+ name: 'alice',
219
236
  token: 'token-a',
220
237
  fiveHourUtilization: 95,
221
238
  blocked: false,
@@ -226,7 +243,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
226
243
  });
227
244
 
228
245
  it('should clear a 5h-origin rejection once the 5h reset has passed', async () => {
229
- mockLoadTokens.mockReturnValue(['token-a']);
246
+ mockLoadTokenEntries.mockReturnValue([
247
+ { name: 'alice', token: 'token-a' },
248
+ ]);
230
249
  mockReadRateLimit.mockReturnValue({
231
250
  fiveHourUtilization: 100,
232
251
  fiveHourReset: pastReset,
@@ -245,6 +264,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
245
264
 
246
265
  expect(result).toEqual([
247
266
  {
267
+ name: 'alice',
248
268
  token: 'token-a',
249
269
  fiveHourUtilization: 0,
250
270
  blocked: false,
@@ -255,7 +275,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
255
275
  });
256
276
 
257
277
  it('should clear a 7d-origin rejection once the 7d reset has passed', async () => {
258
- mockLoadTokens.mockReturnValue(['token-a']);
278
+ mockLoadTokenEntries.mockReturnValue([
279
+ { name: 'alice', token: 'token-a' },
280
+ ]);
259
281
  mockReadRateLimit.mockReturnValue({
260
282
  fiveHourUtilization: 10,
261
283
  fiveHourReset: futureReset,
@@ -274,6 +296,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
274
296
 
275
297
  expect(result).toEqual([
276
298
  {
299
+ name: 'alice',
277
300
  token: 'token-a',
278
301
  fiveHourUtilization: 10,
279
302
  blocked: false,
@@ -284,7 +307,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
284
307
  });
285
308
 
286
309
  it('should keep a 5h-origin rejection while the 5h reset is in the future', async () => {
287
- mockLoadTokens.mockReturnValue(['token-a']);
310
+ mockLoadTokenEntries.mockReturnValue([
311
+ { name: 'alice', token: 'token-a' },
312
+ ]);
288
313
  mockReadRateLimit.mockReturnValue({
289
314
  fiveHourUtilization: 100,
290
315
  fiveHourReset: futureReset,
@@ -303,6 +328,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
303
328
 
304
329
  expect(result).toEqual([
305
330
  {
331
+ name: 'alice',
306
332
  token: 'token-a',
307
333
  fiveHourUtilization: 100,
308
334
  blocked: false,
@@ -313,7 +339,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
313
339
  });
314
340
 
315
341
  it('should keep a still-active 7d rejection after the 5h reset has passed', async () => {
316
- mockLoadTokens.mockReturnValue(['token-a']);
342
+ mockLoadTokenEntries.mockReturnValue([
343
+ { name: 'alice', token: 'token-a' },
344
+ ]);
317
345
  mockReadRateLimit.mockReturnValue({
318
346
  fiveHourUtilization: 100,
319
347
  fiveHourReset: pastReset,
@@ -332,6 +360,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
332
360
 
333
361
  expect(result).toEqual([
334
362
  {
363
+ name: 'alice',
335
364
  token: 'token-a',
336
365
  fiveHourUtilization: 0,
337
366
  blocked: false,
@@ -342,7 +371,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
342
371
  });
343
372
 
344
373
  it('should clear a unified rejection once the 5h reset has passed', async () => {
345
- mockLoadTokens.mockReturnValue(['token-a']);
374
+ mockLoadTokenEntries.mockReturnValue([
375
+ { name: 'alice', token: 'token-a' },
376
+ ]);
346
377
  mockReadRateLimit.mockReturnValue({
347
378
  fiveHourUtilization: 100,
348
379
  fiveHourReset: pastReset,
@@ -361,6 +392,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
361
392
 
362
393
  expect(result).toEqual([
363
394
  {
395
+ name: 'alice',
364
396
  token: 'token-a',
365
397
  fiveHourUtilization: 0,
366
398
  blocked: false,
@@ -371,7 +403,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
371
403
  });
372
404
 
373
405
  it('should default rejected to false when no snapshot exists', async () => {
374
- mockLoadTokens.mockReturnValue(['token-a']);
406
+ mockLoadTokenEntries.mockReturnValue([
407
+ { name: 'alice', token: 'token-a' },
408
+ ]);
375
409
  mockReadRateLimit.mockReturnValue(null);
376
410
  const repository = new ProxyClaudeTokenUsageRepository('/tokens.json');
377
411
 
@@ -379,6 +413,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
379
413
 
380
414
  expect(result).toEqual([
381
415
  {
416
+ name: 'alice',
382
417
  token: 'token-a',
383
418
  fiveHourUtilization: 0,
384
419
  blocked: false,
@@ -389,7 +424,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
389
424
  });
390
425
 
391
426
  it('should keep a model weekly rejection while its reset is in the future', async () => {
392
- mockLoadTokens.mockReturnValue(['token-a']);
427
+ mockLoadTokenEntries.mockReturnValue([
428
+ { name: 'alice', token: 'token-a' },
429
+ ]);
393
430
  mockReadRateLimit.mockReturnValue({
394
431
  fiveHourUtilization: 5,
395
432
  fiveHourReset: futureReset,
@@ -410,6 +447,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
410
447
 
411
448
  expect(result).toEqual([
412
449
  {
450
+ name: 'alice',
413
451
  token: 'token-a',
414
452
  fiveHourUtilization: 5,
415
453
  blocked: false,
@@ -422,7 +460,9 @@ describe('ProxyClaudeTokenUsageRepository', () => {
422
460
  });
423
461
 
424
462
  it('should clear a model weekly rejection once its reset has passed', async () => {
425
- mockLoadTokens.mockReturnValue(['token-a']);
463
+ mockLoadTokenEntries.mockReturnValue([
464
+ { name: 'alice', token: 'token-a' },
465
+ ]);
426
466
  mockReadRateLimit.mockReturnValue({
427
467
  fiveHourUtilization: 5,
428
468
  fiveHourReset: futureReset,
@@ -443,6 +483,7 @@ describe('ProxyClaudeTokenUsageRepository', () => {
443
483
 
444
484
  expect(result).toEqual([
445
485
  {
486
+ name: 'alice',
446
487
  token: 'token-a',
447
488
  fiveHourUtilization: 5,
448
489
  blocked: false,
@@ -2,7 +2,7 @@ import { ClaudeTokenUsage } from '../../domain/entities/ClaudeTokenUsage';
2
2
  import { ClaudeTokenUsageRepository } from '../../domain/usecases/adapter-interfaces/ClaudeTokenUsageRepository';
3
3
  import { ensureProxyRunning } from '../proxy/ensureProxyRunning';
4
4
  import { PROXY_PORT, readRateLimit } from '../proxy/RateLimitCache';
5
- import { loadTokens } from '../proxy/TokenListLoader';
5
+ import { loadTokenEntries } from '../proxy/TokenListLoader';
6
6
 
7
7
  export class ProxyClaudeTokenUsageRepository implements ClaudeTokenUsageRepository {
8
8
  constructor(
@@ -18,15 +18,16 @@ export class ProxyClaudeTokenUsageRepository implements ClaudeTokenUsageReposito
18
18
  if (this.tokenListJsonPath === null) {
19
19
  return [];
20
20
  }
21
- const tokens = loadTokens(this.tokenListJsonPath);
22
- if (tokens === null) {
21
+ const entries = loadTokenEntries(this.tokenListJsonPath);
22
+ if (entries === null) {
23
23
  return [];
24
24
  }
25
25
  const nowEpochSeconds = Date.now() / 1000;
26
- return tokens.map((token) => {
26
+ return entries.map(({ name, token }) => {
27
27
  const snapshot = readRateLimit(token);
28
28
  if (snapshot === null) {
29
29
  return {
30
+ name,
30
31
  token,
31
32
  fiveHourUtilization: 0,
32
33
  blocked: false,
@@ -63,6 +64,7 @@ export class ProxyClaudeTokenUsageRepository implements ClaudeTokenUsageReposito
63
64
  };
64
65
  }
65
66
  return {
67
+ name,
66
68
  token,
67
69
  fiveHourUtilization,
68
70
  blocked: snapshot.blocked,
@@ -27,6 +27,7 @@ type TimelineItem = {
27
27
  number?: number;
28
28
  state?: string;
29
29
  createdAt?: string;
30
+ isDraft?: boolean;
30
31
  mergeable?: string;
31
32
  headRefName?: string;
32
33
  baseRefName?: string;
@@ -117,6 +118,7 @@ type IssueTimelineResponse = {
117
118
  };
118
119
 
119
120
  type PrStatusComputationData = {
121
+ isDraft?: boolean;
120
122
  mergeable?: string;
121
123
  baseRepository?: {
122
124
  branchProtectionRules?: {
@@ -293,21 +295,9 @@ export class ApiV3CheerioRestIssueRepository
293
295
  'getLatest' | 'set'
294
296
  >,
295
297
  readonly localStorageRepository: LocalStorageRepository,
296
- readonly jsonFilePath: string = './tmp/github.com.cookies.json',
297
298
  readonly ghToken: string = process.env.GH_TOKEN || 'dummy',
298
- readonly ghUserName: string | undefined = process.env.GH_USER_NAME,
299
- readonly ghUserPassword: string | undefined = process.env.GH_USER_PASSWORD,
300
- readonly ghAuthenticatorKey: string | undefined = process.env
301
- .GH_AUTHENTICATOR_KEY,
302
299
  ) {
303
- super(
304
- localStorageRepository,
305
- jsonFilePath,
306
- ghToken,
307
- ghUserName,
308
- ghUserPassword,
309
- ghAuthenticatorKey,
310
- );
300
+ super(localStorageRepository, ghToken);
311
301
  }
312
302
 
313
303
  updateStatus: (
@@ -474,6 +464,23 @@ export class ApiV3CheerioRestIssueRepository
474
464
  labels,
475
465
  );
476
466
  };
467
+ searchIssue = async (query: {
468
+ owner: string;
469
+ repositoryName: string;
470
+ type?: 'issue' | 'pr';
471
+ state?: 'open' | 'closed' | 'all';
472
+ title?: string;
473
+ createdFrom?: string;
474
+ assignee?: string;
475
+ }): Promise<
476
+ {
477
+ url: string;
478
+ title: string;
479
+ number: string;
480
+ }[]
481
+ > => {
482
+ return await this.apiV3IssueRepository.searchIssue(query);
483
+ };
477
484
  updateIssue = async (issue: Issue): Promise<void> => {
478
485
  await this.restIssueRepository.updateIssue(issue);
479
486
  };
@@ -732,6 +739,7 @@ export class ApiV3CheerioRestIssueRepository
732
739
  url: prUrl,
733
740
  branchName: headRefName ?? null,
734
741
  createdAt: new Date(0),
742
+ isDraft: data.isDraft === true,
735
743
  isConflicted,
736
744
  isPassedAllCiJob,
737
745
  isCiStateSuccess,
@@ -771,6 +779,7 @@ export class ApiV3CheerioRestIssueRepository
771
779
  number
772
780
  state
773
781
  createdAt
782
+ isDraft
774
783
  mergeable
775
784
  headRefName
776
785
  baseRefName
@@ -959,6 +968,7 @@ export class ApiV3CheerioRestIssueRepository
959
968
  pullRequest(number: $prNumber) {
960
969
  url
961
970
  state
971
+ isDraft
962
972
  headRefName
963
973
  baseRefName
964
974
  mergeable
@@ -7,7 +7,6 @@ describe('ApiV3IssueRepository', () => {
7
7
  const localStorageRepository = new LocalStorageRepository();
8
8
  const repository = new ApiV3IssueRepository(
9
9
  localStorageRepository,
10
- '',
11
10
  process.env.GH_TOKEN || 'dummy',
12
11
  );
13
12
  test('searchIssue', async () => {
@@ -83,7 +83,6 @@ describe('GraphqlProjectItemRepository', () => {
83
83
  const localStorageRepository = new LocalStorageRepository();
84
84
  const repository = new GraphqlProjectItemRepository(
85
85
  localStorageRepository,
86
- '',
87
86
  'dummy-token',
88
87
  );
89
88
 
@@ -109,7 +108,6 @@ describe('GraphqlProjectItemRepository', () => {
109
108
  const localStorageRepository = new LocalStorageRepository();
110
109
  const repository = new GraphqlProjectItemRepository(
111
110
  localStorageRepository,
112
- '',
113
111
  'dummy-token',
114
112
  );
115
113
 
@@ -157,7 +155,6 @@ describe('GraphqlProjectItemRepository', () => {
157
155
  const localStorageRepository = new LocalStorageRepository();
158
156
  const repository = new GraphqlProjectItemRepository(
159
157
  localStorageRepository,
160
- '',
161
158
  'dummy-token',
162
159
  );
163
160
 
@@ -176,7 +173,6 @@ describe('GraphqlProjectItemRepository', () => {
176
173
  const localStorageRepository = new LocalStorageRepository();
177
174
  const repository = new GraphqlProjectItemRepository(
178
175
  localStorageRepository,
179
- '',
180
176
  'dummy-token',
181
177
  );
182
178
 
@@ -195,7 +191,6 @@ describe('GraphqlProjectItemRepository', () => {
195
191
  const localStorageRepository = new LocalStorageRepository();
196
192
  const repository = new GraphqlProjectItemRepository(
197
193
  localStorageRepository,
198
- '',
199
194
  'dummy-token',
200
195
  );
201
196
 
@@ -244,7 +239,6 @@ describe('GraphqlProjectItemRepository', () => {
244
239
  const localStorageRepository = new LocalStorageRepository();
245
240
  const repository = new GraphqlProjectItemRepository(
246
241
  localStorageRepository,
247
- '',
248
242
  'dummy-token',
249
243
  );
250
244
 
@@ -277,7 +271,6 @@ describe('GraphqlProjectItemRepository', () => {
277
271
  const localStorageRepository = new LocalStorageRepository();
278
272
  const repository = new GraphqlProjectItemRepository(
279
273
  localStorageRepository,
280
- '',
281
274
  'dummy-token',
282
275
  );
283
276
 
@@ -305,7 +298,6 @@ describe('GraphqlProjectItemRepository', () => {
305
298
  const localStorageRepository = new LocalStorageRepository();
306
299
  const repository = new GraphqlProjectItemRepository(
307
300
  localStorageRepository,
308
- '',
309
301
  'dummy-token',
310
302
  );
311
303
 
@@ -6,7 +6,6 @@ describe('RestIssueRepository', () => {
6
6
  const localStorageRepository = new LocalStorageRepository();
7
7
  const restIssueRepository: RestIssueRepository = new RestIssueRepository(
8
8
  localStorageRepository,
9
- '',
10
9
  process.env.GH_TOKEN || 'dummy',
11
10
  );
12
11
 
@@ -4,6 +4,7 @@ export type ClaudeModelWeeklyLimit = {
4
4
  };
5
5
 
6
6
  export type ClaudeTokenUsage = {
7
+ name?: string;
7
8
  token: string;
8
9
  fiveHourUtilization: number;
9
10
  blocked: boolean;
@@ -159,6 +159,9 @@ describe('HandleScheduledEventUseCase', () => {
159
159
  ['LastExecutionDateTime'],
160
160
  ['2024-01-01T00:00:00Z'],
161
161
  ]);
162
+ mockStartPreparationUseCase.run.mockResolvedValue({
163
+ rotationOrder: null,
164
+ });
162
165
  });
163
166
 
164
167
  it('should call AnalyzeProblemByIssueUseCase with correct parameters', async () => {
@@ -377,6 +380,7 @@ describe('HandleScheduledEventUseCase', () => {
377
380
  });
378
381
  mockStartPreparationUseCase.run.mockImplementation(async () => {
379
382
  callOrder.push('startPreparation');
383
+ return { rotationOrder: null };
380
384
  });
381
385
 
382
386
  const input = {
@@ -19,7 +19,10 @@ import { SetNoStoryIssueToStoryUseCase } from './SetNoStoryIssueToStoryUseCase';
19
19
  import { CreateNewStoryByLabelUseCase } from './CreateNewStoryByLabelUseCase';
20
20
  import { AssignNoAssigneeIssueToManagerUseCase } from './AssignNoAssigneeIssueToManagerUseCase';
21
21
  import { UpdateIssueStatusByLabelUseCase } from './UpdateIssueStatusByLabelUseCase';
22
- import { StartPreparationUseCase } from './StartPreparationUseCase';
22
+ import {
23
+ RotationOrderEntry,
24
+ StartPreparationUseCase,
25
+ } from './StartPreparationUseCase';
23
26
  import { RevertOrphanedPreparationUseCase } from './RevertOrphanedPreparationUseCase';
24
27
  import { RevertNotReadyAwaitingQualityCheckUseCase } from './RevertNotReadyAwaitingQualityCheckUseCase';
25
28
  import { SetupTowerDefenceProjectUseCase } from './SetupTowerDefenceProjectUseCase';
@@ -96,6 +99,7 @@ export class HandleScheduledEventUseCase {
96
99
  cacheUsed: boolean;
97
100
  targetDateTimes: Date[];
98
101
  storyIssues: StoryObjectMap;
102
+ rotationOrder: RotationOrderEntry[] | null;
99
103
  } | null> => {
100
104
  if (input.disabled) {
101
105
  return null;
@@ -205,8 +209,9 @@ export class HandleScheduledEventUseCase {
205
209
  now,
206
210
  );
207
211
 
212
+ let rotationOrder: RotationOrderEntry[] | null = null;
208
213
  try {
209
- await this.runEachUseCases(
214
+ const useCaseResult = await this.runEachUseCases(
210
215
  input,
211
216
  project,
212
217
  issues,
@@ -215,6 +220,7 @@ export class HandleScheduledEventUseCase {
215
220
  storyIssues,
216
221
  runSlowSweep,
217
222
  );
223
+ rotationOrder = useCaseResult.rotationOrder;
218
224
  } catch (e) {
219
225
  if (!(e instanceof Error)) {
220
226
  throw e;
@@ -238,7 +244,14 @@ ${JSON.stringify(e)}
238
244
  throw e;
239
245
  }
240
246
 
241
- return { project, issues, cacheUsed, targetDateTimes, storyIssues };
247
+ return {
248
+ project,
249
+ issues,
250
+ cacheUsed,
251
+ targetDateTimes,
252
+ storyIssues,
253
+ rotationOrder,
254
+ };
242
255
  };
243
256
  runEachUseCases = async (
244
257
  input: Parameters<HandleScheduledEventUseCase['run']>[0],
@@ -248,7 +261,7 @@ ${JSON.stringify(e)}
248
261
  targetDateTimes: Date[],
249
262
  storyObjectMap: StoryObjectMap,
250
263
  runSlowSweep: boolean,
251
- ): Promise<void> => {
264
+ ): Promise<{ rotationOrder: RotationOrderEntry[] | null }> => {
252
265
  if (runSlowSweep) {
253
266
  await this.runSlowSweepUseCases(
254
267
  input,
@@ -284,7 +297,7 @@ ${JSON.stringify(e)}
284
297
  input.startPreparation.awaitingQualityCheckStatus ?? undefined,
285
298
  });
286
299
  }
287
- await this.startPreparationUseCase.run({
300
+ const preparationResult = await this.startPreparationUseCase.run({
288
301
  projectUrl: input.projectUrl,
289
302
  defaultAgentName: input.startPreparation.defaultAgentName,
290
303
  defaultLlmModelName: input.startPreparation.defaultLlmModelName ?? null,
@@ -298,7 +311,9 @@ ${JSON.stringify(e)}
298
311
  codexHomeCandidates: input.startPreparation.codexHomeCandidates ?? null,
299
312
  allowIssueCacheMinutes: input.allowIssueCacheMinutes,
300
313
  });
314
+ return { rotationOrder: preparationResult.rotationOrder };
301
315
  }
316
+ return { rotationOrder: null };
302
317
  };
303
318
  runSlowSweepUseCases = async (
304
319
  input: Parameters<HandleScheduledEventUseCase['run']>[0],