github-issue-tower-defence-management 1.68.0 → 1.69.0

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 (25) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +5 -0
  3. package/bin/adapter/entry-points/cli/index.js +1 -0
  4. package/bin/adapter/entry-points/cli/index.js.map +1 -1
  5. package/bin/adapter/entry-points/cli/projectConfig.js +4 -0
  6. package/bin/adapter/entry-points/cli/projectConfig.js.map +1 -1
  7. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +20 -8
  8. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  9. package/bin/domain/usecases/HandleScheduledEventUseCase.js +1 -0
  10. package/bin/domain/usecases/HandleScheduledEventUseCase.js.map +1 -1
  11. package/bin/domain/usecases/StartPreparationUseCase.js +6 -0
  12. package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -1
  13. package/package.json +1 -1
  14. package/src/adapter/entry-points/cli/index.test.ts +2 -0
  15. package/src/adapter/entry-points/cli/index.ts +1 -0
  16. package/src/adapter/entry-points/cli/projectConfig.ts +6 -0
  17. package/src/domain/usecases/HandleScheduledEventUseCase.ts +3 -0
  18. package/src/domain/usecases/StartPreparationUseCase.test.ts +198 -0
  19. package/src/domain/usecases/StartPreparationUseCase.ts +10 -0
  20. package/types/adapter/entry-points/cli/projectConfig.d.ts +1 -0
  21. package/types/adapter/entry-points/cli/projectConfig.d.ts.map +1 -1
  22. package/types/domain/usecases/HandleScheduledEventUseCase.d.ts +1 -0
  23. package/types/domain/usecases/HandleScheduledEventUseCase.d.ts.map +1 -1
  24. package/types/domain/usecases/StartPreparationUseCase.d.ts +1 -0
  25. package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -1
@@ -153,6 +153,7 @@ describe('StartPreparationUseCase', () => {
153
153
  allowedIssueAuthors: null,
154
154
  codexHomeCandidates: null,
155
155
  allowIssueCacheMinutes: 0,
156
+ labelsAsLlmAgentName: null,
156
157
  });
157
158
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
158
159
  expect(mockIssueRepository.updateStatus.mock.calls[0][0]).toBe(mockProject);
@@ -217,6 +218,7 @@ describe('StartPreparationUseCase', () => {
217
218
  allowedIssueAuthors: null,
218
219
  codexHomeCandidates: null,
219
220
  allowIssueCacheMinutes: 0,
221
+ labelsAsLlmAgentName: null,
220
222
  });
221
223
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
222
224
  expect(mockLocalCommandRunner.runCommand.mock.calls[0]).toEqual([
@@ -273,6 +275,7 @@ describe('StartPreparationUseCase', () => {
273
275
  allowedIssueAuthors: null,
274
276
  codexHomeCandidates: null,
275
277
  allowIssueCacheMinutes: 0,
278
+ labelsAsLlmAgentName: null,
276
279
  });
277
280
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
278
281
  expect(mockLocalCommandRunner.runCommand.mock.calls[0]).toEqual([
@@ -320,6 +323,7 @@ describe('StartPreparationUseCase', () => {
320
323
  allowedIssueAuthors: null,
321
324
  codexHomeCandidates: null,
322
325
  allowIssueCacheMinutes: 0,
326
+ labelsAsLlmAgentName: null,
323
327
  });
324
328
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(0);
325
329
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(0);
@@ -368,6 +372,7 @@ describe('StartPreparationUseCase', () => {
368
372
  allowedIssueAuthors: null,
369
373
  codexHomeCandidates: null,
370
374
  allowIssueCacheMinutes: 0,
375
+ labelsAsLlmAgentName: null,
371
376
  });
372
377
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(0);
373
378
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(0);
@@ -415,6 +420,7 @@ describe('StartPreparationUseCase', () => {
415
420
  allowedIssueAuthors: null,
416
421
  codexHomeCandidates: null,
417
422
  allowIssueCacheMinutes: 0,
423
+ labelsAsLlmAgentName: null,
418
424
  });
419
425
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(0);
420
426
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(0);
@@ -480,6 +486,7 @@ describe('StartPreparationUseCase', () => {
480
486
  allowedIssueAuthors: null,
481
487
  codexHomeCandidates: null,
482
488
  allowIssueCacheMinutes: 0,
489
+ labelsAsLlmAgentName: null,
483
490
  });
484
491
  expect(mockIssueRepository.closePullRequest).toHaveBeenCalledWith(
485
492
  newerPR.url,
@@ -568,6 +575,7 @@ describe('StartPreparationUseCase', () => {
568
575
  allowedIssueAuthors: null,
569
576
  codexHomeCandidates: null,
570
577
  allowIssueCacheMinutes: 0,
578
+ labelsAsLlmAgentName: null,
571
579
  });
572
580
  expect(mockIssueRepository.closePullRequest).toHaveBeenCalledWith(
573
581
  newerPR.url,
@@ -630,6 +638,7 @@ describe('StartPreparationUseCase', () => {
630
638
  allowedIssueAuthors: null,
631
639
  codexHomeCandidates: null,
632
640
  allowIssueCacheMinutes: 0,
641
+ labelsAsLlmAgentName: null,
633
642
  });
634
643
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(0);
635
644
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(0);
@@ -673,6 +682,7 @@ describe('StartPreparationUseCase', () => {
673
682
  allowedIssueAuthors: null,
674
683
  codexHomeCandidates: null,
675
684
  allowIssueCacheMinutes: 0,
685
+ labelsAsLlmAgentName: null,
676
686
  });
677
687
  // Both awaiting issues should be updated (forward iteration: url1 first, then url2)
678
688
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(2);
@@ -729,6 +739,7 @@ describe('StartPreparationUseCase', () => {
729
739
  allowedIssueAuthors: null,
730
740
  codexHomeCandidates: null,
731
741
  allowIssueCacheMinutes: 0,
742
+ labelsAsLlmAgentName: null,
732
743
  });
733
744
  // Loop doesn't run because we're already at max (6 >= 6)
734
745
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(0);
@@ -763,6 +774,7 @@ describe('StartPreparationUseCase', () => {
763
774
  allowedIssueAuthors: null,
764
775
  codexHomeCandidates: null,
765
776
  allowIssueCacheMinutes: 0,
777
+ labelsAsLlmAgentName: null,
766
778
  });
767
779
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
768
780
  expect(mockLocalCommandRunner.runCommand.mock.calls[0]).toEqual([
@@ -807,6 +819,7 @@ describe('StartPreparationUseCase', () => {
807
819
  allowedIssueAuthors: null,
808
820
  codexHomeCandidates: null,
809
821
  allowIssueCacheMinutes: 0,
822
+ labelsAsLlmAgentName: null,
810
823
  });
811
824
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
812
825
  expect(mockLocalCommandRunner.runCommand.mock.calls[0]).toEqual([
@@ -851,6 +864,7 @@ describe('StartPreparationUseCase', () => {
851
864
  allowedIssueAuthors: null,
852
865
  codexHomeCandidates: null,
853
866
  allowIssueCacheMinutes: 0,
867
+ labelsAsLlmAgentName: null,
854
868
  });
855
869
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
856
870
  expect(mockLocalCommandRunner.runCommand.mock.calls[0]).toEqual([
@@ -895,6 +909,7 @@ describe('StartPreparationUseCase', () => {
895
909
  allowedIssueAuthors: null,
896
910
  codexHomeCandidates: null,
897
911
  allowIssueCacheMinutes: 0,
912
+ labelsAsLlmAgentName: null,
898
913
  });
899
914
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
900
915
  expect(mockLocalCommandRunner.runCommand.mock.calls[0]).toEqual([
@@ -939,6 +954,7 @@ describe('StartPreparationUseCase', () => {
939
954
  allowedIssueAuthors: null,
940
955
  codexHomeCandidates: null,
941
956
  allowIssueCacheMinutes: 0,
957
+ labelsAsLlmAgentName: null,
942
958
  });
943
959
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
944
960
  expect(mockLocalCommandRunner.runCommand.mock.calls[0]).toEqual([
@@ -983,6 +999,7 @@ describe('StartPreparationUseCase', () => {
983
999
  allowedIssueAuthors: null,
984
1000
  codexHomeCandidates: null,
985
1001
  allowIssueCacheMinutes: 0,
1002
+ labelsAsLlmAgentName: null,
986
1003
  });
987
1004
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
988
1005
  expect(mockLocalCommandRunner.runCommand.mock.calls[0]).toEqual([
@@ -1030,6 +1047,7 @@ describe('StartPreparationUseCase', () => {
1030
1047
  allowedIssueAuthors: null,
1031
1048
  codexHomeCandidates: null,
1032
1049
  allowIssueCacheMinutes: 0,
1050
+ labelsAsLlmAgentName: null,
1033
1051
  });
1034
1052
  expect(consoleErrorSpy).toHaveBeenCalledWith(
1035
1053
  'No LLM model configured for issue url1. Provide --defaultLlmModelName or add an llm-model: label.',
@@ -1077,6 +1095,7 @@ describe('StartPreparationUseCase', () => {
1077
1095
  allowedIssueAuthors: null,
1078
1096
  codexHomeCandidates: null,
1079
1097
  allowIssueCacheMinutes: 0,
1098
+ labelsAsLlmAgentName: null,
1080
1099
  });
1081
1100
  expect(consoleErrorSpy).toHaveBeenCalledWith(
1082
1101
  'No LLM model configured for issue url1. Provide --defaultLlmModelName or add an llm-model: label.',
@@ -1126,6 +1145,7 @@ describe('StartPreparationUseCase', () => {
1126
1145
  allowedIssueAuthors: null,
1127
1146
  codexHomeCandidates: null,
1128
1147
  allowIssueCacheMinutes: 0,
1148
+ labelsAsLlmAgentName: null,
1129
1149
  });
1130
1150
  // No issues are in 'Awaiting Workspace' status, so no updates should happen
1131
1151
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(0);
@@ -1160,6 +1180,7 @@ describe('StartPreparationUseCase', () => {
1160
1180
  allowedIssueAuthors: null,
1161
1181
  codexHomeCandidates: null,
1162
1182
  allowIssueCacheMinutes: 0,
1183
+ labelsAsLlmAgentName: null,
1163
1184
  });
1164
1185
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(3);
1165
1186
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(3);
@@ -1193,6 +1214,7 @@ describe('StartPreparationUseCase', () => {
1193
1214
  allowedIssueAuthors: null,
1194
1215
  codexHomeCandidates: null,
1195
1216
  allowIssueCacheMinutes: 0,
1217
+ labelsAsLlmAgentName: null,
1196
1218
  });
1197
1219
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(6);
1198
1220
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(6);
@@ -1249,6 +1271,7 @@ describe('StartPreparationUseCase', () => {
1249
1271
  allowedIssueAuthors: null,
1250
1272
  codexHomeCandidates: null,
1251
1273
  allowIssueCacheMinutes: 0,
1274
+ labelsAsLlmAgentName: null,
1252
1275
  });
1253
1276
 
1254
1277
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(12);
@@ -1314,6 +1337,7 @@ describe('StartPreparationUseCase', () => {
1314
1337
  allowedIssueAuthors: null,
1315
1338
  codexHomeCandidates: null,
1316
1339
  allowIssueCacheMinutes: 0,
1340
+ labelsAsLlmAgentName: null,
1317
1341
  });
1318
1342
 
1319
1343
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(12);
@@ -1377,6 +1401,7 @@ describe('StartPreparationUseCase', () => {
1377
1401
  allowedIssueAuthors: null,
1378
1402
  codexHomeCandidates: null,
1379
1403
  allowIssueCacheMinutes: 0,
1404
+ labelsAsLlmAgentName: null,
1380
1405
  });
1381
1406
 
1382
1407
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(2);
@@ -1424,6 +1449,7 @@ describe('StartPreparationUseCase', () => {
1424
1449
  allowedIssueAuthors: null,
1425
1450
  codexHomeCandidates: null,
1426
1451
  allowIssueCacheMinutes: 0,
1452
+ labelsAsLlmAgentName: null,
1427
1453
  });
1428
1454
 
1429
1455
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
@@ -1478,6 +1504,7 @@ describe('StartPreparationUseCase', () => {
1478
1504
  allowedIssueAuthors: null,
1479
1505
  codexHomeCandidates: null,
1480
1506
  allowIssueCacheMinutes: 0,
1507
+ labelsAsLlmAgentName: null,
1481
1508
  });
1482
1509
 
1483
1510
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
@@ -1535,6 +1562,7 @@ describe('StartPreparationUseCase', () => {
1535
1562
  allowedIssueAuthors: null,
1536
1563
  codexHomeCandidates: null,
1537
1564
  allowIssueCacheMinutes: 0,
1565
+ labelsAsLlmAgentName: null,
1538
1566
  });
1539
1567
 
1540
1568
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
@@ -1582,6 +1610,7 @@ describe('StartPreparationUseCase', () => {
1582
1610
  allowedIssueAuthors: null,
1583
1611
  codexHomeCandidates: null,
1584
1612
  allowIssueCacheMinutes: 0,
1613
+ labelsAsLlmAgentName: null,
1585
1614
  });
1586
1615
 
1587
1616
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
@@ -1629,6 +1658,7 @@ describe('StartPreparationUseCase', () => {
1629
1658
  allowedIssueAuthors: null,
1630
1659
  codexHomeCandidates: null,
1631
1660
  allowIssueCacheMinutes: 0,
1661
+ labelsAsLlmAgentName: null,
1632
1662
  });
1633
1663
 
1634
1664
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
@@ -1676,6 +1706,7 @@ describe('StartPreparationUseCase', () => {
1676
1706
  allowedIssueAuthors: null,
1677
1707
  codexHomeCandidates: null,
1678
1708
  allowIssueCacheMinutes: 0,
1709
+ labelsAsLlmAgentName: null,
1679
1710
  });
1680
1711
 
1681
1712
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
@@ -1729,6 +1760,7 @@ describe('StartPreparationUseCase', () => {
1729
1760
  allowedIssueAuthors: ['user1', 'user2'],
1730
1761
  codexHomeCandidates: null,
1731
1762
  allowIssueCacheMinutes: 0,
1763
+ labelsAsLlmAgentName: null,
1732
1764
  });
1733
1765
 
1734
1766
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
@@ -1776,6 +1808,7 @@ describe('StartPreparationUseCase', () => {
1776
1808
  allowedIssueAuthors: null,
1777
1809
  codexHomeCandidates: null,
1778
1810
  allowIssueCacheMinutes: 0,
1811
+ labelsAsLlmAgentName: null,
1779
1812
  });
1780
1813
 
1781
1814
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(2);
@@ -1821,6 +1854,7 @@ describe('StartPreparationUseCase', () => {
1821
1854
  allowedIssueAuthors: ['user1', 'user2'],
1822
1855
  codexHomeCandidates: null,
1823
1856
  allowIssueCacheMinutes: 0,
1857
+ labelsAsLlmAgentName: null,
1824
1858
  });
1825
1859
 
1826
1860
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
@@ -1860,6 +1894,7 @@ describe('StartPreparationUseCase', () => {
1860
1894
  allowedIssueAuthors: ['user1'],
1861
1895
  codexHomeCandidates: null,
1862
1896
  allowIssueCacheMinutes: 0,
1897
+ labelsAsLlmAgentName: null,
1863
1898
  });
1864
1899
 
1865
1900
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(0);
@@ -1896,6 +1931,7 @@ describe('StartPreparationUseCase', () => {
1896
1931
  allowedIssueAuthors: null,
1897
1932
  codexHomeCandidates: null,
1898
1933
  allowIssueCacheMinutes: 0,
1934
+ labelsAsLlmAgentName: null,
1899
1935
  });
1900
1936
 
1901
1937
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -1943,6 +1979,7 @@ describe('StartPreparationUseCase', () => {
1943
1979
  allowedIssueAuthors: null,
1944
1980
  codexHomeCandidates: [],
1945
1981
  allowIssueCacheMinutes: 0,
1982
+ labelsAsLlmAgentName: null,
1946
1983
  });
1947
1984
 
1948
1985
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -1990,6 +2027,7 @@ describe('StartPreparationUseCase', () => {
1990
2027
  allowedIssueAuthors: null,
1991
2028
  codexHomeCandidates: ['.codex-dev1'],
1992
2029
  allowIssueCacheMinutes: 0,
2030
+ labelsAsLlmAgentName: null,
1993
2031
  });
1994
2032
 
1995
2033
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -2057,6 +2095,7 @@ describe('StartPreparationUseCase', () => {
2057
2095
  allowedIssueAuthors: null,
2058
2096
  codexHomeCandidates: ['.codex-dev1', '.codex-dev2'],
2059
2097
  allowIssueCacheMinutes: 0,
2098
+ labelsAsLlmAgentName: null,
2060
2099
  });
2061
2100
 
2062
2101
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(3);
@@ -2121,6 +2160,7 @@ describe('StartPreparationUseCase', () => {
2121
2160
  allowedIssueAuthors: null,
2122
2161
  codexHomeCandidates: null,
2123
2162
  allowIssueCacheMinutes: 0,
2163
+ labelsAsLlmAgentName: null,
2124
2164
  });
2125
2165
 
2126
2166
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
@@ -2179,6 +2219,7 @@ describe('StartPreparationUseCase', () => {
2179
2219
  allowedIssueAuthors: null,
2180
2220
  codexHomeCandidates: null,
2181
2221
  allowIssueCacheMinutes: 0,
2222
+ labelsAsLlmAgentName: null,
2182
2223
  });
2183
2224
 
2184
2225
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(0);
@@ -2206,6 +2247,7 @@ describe('StartPreparationUseCase', () => {
2206
2247
  allowedIssueAuthors: null,
2207
2248
  codexHomeCandidates: null,
2208
2249
  allowIssueCacheMinutes: 5,
2250
+ labelsAsLlmAgentName: null,
2209
2251
  });
2210
2252
 
2211
2253
  expect(mockIssueRepository.getStoryObjectMap).toHaveBeenCalledWith(
@@ -2252,6 +2294,7 @@ describe('StartPreparationUseCase', () => {
2252
2294
  allowedIssueAuthors: null,
2253
2295
  codexHomeCandidates: null,
2254
2296
  allowIssueCacheMinutes: 0,
2297
+ labelsAsLlmAgentName: null,
2255
2298
  });
2256
2299
 
2257
2300
  expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
@@ -2301,6 +2344,7 @@ describe('StartPreparationUseCase', () => {
2301
2344
  allowedIssueAuthors: null,
2302
2345
  codexHomeCandidates: null,
2303
2346
  allowIssueCacheMinutes: 0,
2347
+ labelsAsLlmAgentName: null,
2304
2348
  });
2305
2349
 
2306
2350
  expect(
@@ -2386,6 +2430,7 @@ describe('StartPreparationUseCase', () => {
2386
2430
  allowedIssueAuthors: null,
2387
2431
  codexHomeCandidates: null,
2388
2432
  allowIssueCacheMinutes: 0,
2433
+ labelsAsLlmAgentName: null,
2389
2434
  });
2390
2435
 
2391
2436
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(3);
@@ -2442,6 +2487,7 @@ describe('StartPreparationUseCase', () => {
2442
2487
  allowedIssueAuthors: null,
2443
2488
  codexHomeCandidates: null,
2444
2489
  allowIssueCacheMinutes: 0,
2490
+ labelsAsLlmAgentName: null,
2445
2491
  });
2446
2492
 
2447
2493
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -2512,6 +2558,7 @@ describe('StartPreparationUseCase', () => {
2512
2558
  allowedIssueAuthors: null,
2513
2559
  codexHomeCandidates: null,
2514
2560
  allowIssueCacheMinutes: 0,
2561
+ labelsAsLlmAgentName: null,
2515
2562
  });
2516
2563
 
2517
2564
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -2573,6 +2620,7 @@ describe('StartPreparationUseCase', () => {
2573
2620
  allowedIssueAuthors: null,
2574
2621
  codexHomeCandidates: null,
2575
2622
  allowIssueCacheMinutes: 0,
2623
+ labelsAsLlmAgentName: null,
2576
2624
  });
2577
2625
 
2578
2626
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -2637,6 +2685,7 @@ describe('StartPreparationUseCase', () => {
2637
2685
  allowedIssueAuthors: null,
2638
2686
  codexHomeCandidates: null,
2639
2687
  allowIssueCacheMinutes: 0,
2688
+ labelsAsLlmAgentName: null,
2640
2689
  });
2641
2690
 
2642
2691
  expect(
@@ -2704,6 +2753,7 @@ describe('StartPreparationUseCase', () => {
2704
2753
  allowedIssueAuthors: null,
2705
2754
  codexHomeCandidates: null,
2706
2755
  allowIssueCacheMinutes: 0,
2756
+ labelsAsLlmAgentName: null,
2707
2757
  });
2708
2758
 
2709
2759
  expect(
@@ -2811,6 +2861,7 @@ describe('StartPreparationUseCase', () => {
2811
2861
  allowedIssueAuthors: null,
2812
2862
  codexHomeCandidates: null,
2813
2863
  allowIssueCacheMinutes: 0,
2864
+ labelsAsLlmAgentName: null,
2814
2865
  });
2815
2866
 
2816
2867
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(3);
@@ -2868,6 +2919,7 @@ describe('StartPreparationUseCase', () => {
2868
2919
  allowedIssueAuthors: null,
2869
2920
  codexHomeCandidates: null,
2870
2921
  allowIssueCacheMinutes: 0,
2922
+ labelsAsLlmAgentName: null,
2871
2923
  });
2872
2924
 
2873
2925
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(3);
@@ -2929,6 +2981,7 @@ describe('StartPreparationUseCase', () => {
2929
2981
  allowedIssueAuthors: null,
2930
2982
  codexHomeCandidates: null,
2931
2983
  allowIssueCacheMinutes: 0,
2984
+ labelsAsLlmAgentName: null,
2932
2985
  });
2933
2986
 
2934
2987
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -2990,6 +3043,7 @@ describe('StartPreparationUseCase', () => {
2990
3043
  allowedIssueAuthors: null,
2991
3044
  codexHomeCandidates: null,
2992
3045
  allowIssueCacheMinutes: 0,
3046
+ labelsAsLlmAgentName: null,
2993
3047
  });
2994
3048
 
2995
3049
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -3051,6 +3105,7 @@ describe('StartPreparationUseCase', () => {
3051
3105
  allowedIssueAuthors: null,
3052
3106
  codexHomeCandidates: null,
3053
3107
  allowIssueCacheMinutes: 0,
3108
+ labelsAsLlmAgentName: null,
3054
3109
  });
3055
3110
 
3056
3111
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -3115,6 +3170,7 @@ describe('StartPreparationUseCase', () => {
3115
3170
  allowedIssueAuthors: null,
3116
3171
  codexHomeCandidates: null,
3117
3172
  allowIssueCacheMinutes: 0,
3173
+ labelsAsLlmAgentName: null,
3118
3174
  });
3119
3175
 
3120
3176
  expect(
@@ -3161,6 +3217,7 @@ describe('StartPreparationUseCase', () => {
3161
3217
  allowedIssueAuthors: null,
3162
3218
  codexHomeCandidates: null,
3163
3219
  allowIssueCacheMinutes: 0,
3220
+ labelsAsLlmAgentName: null,
3164
3221
  });
3165
3222
 
3166
3223
  expect(
@@ -3225,6 +3282,7 @@ describe('StartPreparationUseCase', () => {
3225
3282
  allowedIssueAuthors: null,
3226
3283
  codexHomeCandidates: null,
3227
3284
  allowIssueCacheMinutes: 0,
3285
+ labelsAsLlmAgentName: null,
3228
3286
  });
3229
3287
 
3230
3288
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -3289,6 +3347,7 @@ describe('StartPreparationUseCase', () => {
3289
3347
  allowedIssueAuthors: null,
3290
3348
  codexHomeCandidates: null,
3291
3349
  allowIssueCacheMinutes: 0,
3350
+ labelsAsLlmAgentName: null,
3292
3351
  });
3293
3352
 
3294
3353
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -3353,6 +3412,7 @@ describe('StartPreparationUseCase', () => {
3353
3412
  allowedIssueAuthors: null,
3354
3413
  codexHomeCandidates: null,
3355
3414
  allowIssueCacheMinutes: 0,
3415
+ labelsAsLlmAgentName: null,
3356
3416
  });
3357
3417
 
3358
3418
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -3417,6 +3477,7 @@ describe('StartPreparationUseCase', () => {
3417
3477
  allowedIssueAuthors: null,
3418
3478
  codexHomeCandidates: null,
3419
3479
  allowIssueCacheMinutes: 0,
3480
+ labelsAsLlmAgentName: null,
3420
3481
  });
3421
3482
 
3422
3483
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -3489,6 +3550,7 @@ describe('StartPreparationUseCase', () => {
3489
3550
  allowedIssueAuthors: null,
3490
3551
  codexHomeCandidates: null,
3491
3552
  allowIssueCacheMinutes: 0,
3553
+ labelsAsLlmAgentName: null,
3492
3554
  });
3493
3555
 
3494
3556
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -3561,6 +3623,7 @@ describe('StartPreparationUseCase', () => {
3561
3623
  allowedIssueAuthors: null,
3562
3624
  codexHomeCandidates: null,
3563
3625
  allowIssueCacheMinutes: 0,
3626
+ labelsAsLlmAgentName: null,
3564
3627
  });
3565
3628
 
3566
3629
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -3638,6 +3701,7 @@ describe('StartPreparationUseCase', () => {
3638
3701
  allowedIssueAuthors: null,
3639
3702
  codexHomeCandidates: null,
3640
3703
  allowIssueCacheMinutes: 0,
3704
+ labelsAsLlmAgentName: null,
3641
3705
  });
3642
3706
 
3643
3707
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(2);
@@ -3705,6 +3769,7 @@ describe('StartPreparationUseCase', () => {
3705
3769
  allowedIssueAuthors: null,
3706
3770
  codexHomeCandidates: null,
3707
3771
  allowIssueCacheMinutes: 0,
3772
+ labelsAsLlmAgentName: null,
3708
3773
  });
3709
3774
 
3710
3775
  expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
@@ -3715,6 +3780,139 @@ describe('StartPreparationUseCase', () => {
3715
3780
  },
3716
3781
  });
3717
3782
  });
3783
+
3784
+ describe('agent selection precedence', () => {
3785
+ const runWithIssueLabels = async (params: {
3786
+ labels: string[];
3787
+ defaultAgentName: string;
3788
+ defaultLlmAgentName: string | null;
3789
+ labelsAsLlmAgentName: string[] | null;
3790
+ }): Promise<string> => {
3791
+ const awaitingIssues: Issue[] = [
3792
+ createMockIssue({
3793
+ url: 'url1',
3794
+ title: 'Issue 1',
3795
+ labels: params.labels,
3796
+ status: 'Awaiting Workspace',
3797
+ }),
3798
+ ];
3799
+ mockProjectRepository.getByUrl.mockResolvedValue(mockProject);
3800
+ mockIssueRepository.getStoryObjectMap.mockResolvedValue(
3801
+ createMockStoryObjectMap(awaitingIssues),
3802
+ );
3803
+ mockLocalCommandRunner.runCommand.mockResolvedValue({
3804
+ stdout: '',
3805
+ stderr: '',
3806
+ exitCode: 0,
3807
+ });
3808
+ await useCase.run({
3809
+ projectUrl: 'https://github.com/user/repo',
3810
+ defaultAgentName: params.defaultAgentName,
3811
+ defaultLlmModelName: 'claude-opus',
3812
+ defaultLlmAgentName: params.defaultLlmAgentName,
3813
+ configFilePath: '/path/to/config.yml',
3814
+ maximumPreparingIssuesCount: null,
3815
+ utilizationPercentageThreshold: 90,
3816
+ allowedIssueAuthors: null,
3817
+ codexHomeCandidates: null,
3818
+ allowIssueCacheMinutes: 0,
3819
+ labelsAsLlmAgentName: params.labelsAsLlmAgentName,
3820
+ });
3821
+ expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
3822
+ const awArgs = mockLocalCommandRunner.runCommand.mock.calls[0][1];
3823
+ return awArgs[1];
3824
+ };
3825
+
3826
+ it('selects explicit llm-agent: label over labelsAsLlmAgentName mapping', async () => {
3827
+ const selectedAgent = await runWithIssueLabels({
3828
+ labels: ['llm-agent:explicit-agent', 'story', 'category:impl'],
3829
+ defaultAgentName: 'default-agent',
3830
+ defaultLlmAgentName: 'default-llm-agent',
3831
+ labelsAsLlmAgentName: ['story'],
3832
+ });
3833
+ expect(selectedAgent).toBe('explicit-agent');
3834
+ });
3835
+
3836
+ it('uses the label name as the agent name when an issue label is listed in labelsAsLlmAgentName, over category: label', async () => {
3837
+ const selectedAgent = await runWithIssueLabels({
3838
+ labels: ['story', 'category:impl'],
3839
+ defaultAgentName: 'default-agent',
3840
+ defaultLlmAgentName: 'default-llm-agent',
3841
+ labelsAsLlmAgentName: ['story'],
3842
+ });
3843
+ expect(selectedAgent).toBe('story');
3844
+ });
3845
+
3846
+ it('matches labelsAsLlmAgentName entries exactly including colons in the label name', async () => {
3847
+ const selectedAgent = await runWithIssueLabels({
3848
+ labels: ['story:body-condition', 'category:impl'],
3849
+ defaultAgentName: 'default-agent',
3850
+ defaultLlmAgentName: 'default-llm-agent',
3851
+ labelsAsLlmAgentName: ['story', 'story:body-condition'],
3852
+ });
3853
+ expect(selectedAgent).toBe('story:body-condition');
3854
+ });
3855
+
3856
+ it('falls through to category: label when no llm-agent: label and no issue label is in labelsAsLlmAgentName', async () => {
3857
+ const selectedAgent = await runWithIssueLabels({
3858
+ labels: ['unrelated-label', 'category:impl'],
3859
+ defaultAgentName: 'default-agent',
3860
+ defaultLlmAgentName: 'default-llm-agent',
3861
+ labelsAsLlmAgentName: ['story'],
3862
+ });
3863
+ expect(selectedAgent).toBe('impl');
3864
+ });
3865
+
3866
+ it('falls through to defaultLlmAgentName when no llm-agent:, no labelsAsLlmAgentName match, and no category: label', async () => {
3867
+ const selectedAgent = await runWithIssueLabels({
3868
+ labels: ['unrelated-label'],
3869
+ defaultAgentName: 'default-agent',
3870
+ defaultLlmAgentName: 'default-llm-agent',
3871
+ labelsAsLlmAgentName: ['story'],
3872
+ });
3873
+ expect(selectedAgent).toBe('default-llm-agent');
3874
+ });
3875
+
3876
+ it('falls through to defaultAgentName when no llm-agent:, no labelsAsLlmAgentName match, no category: label, and no defaultLlmAgentName', async () => {
3877
+ const selectedAgent = await runWithIssueLabels({
3878
+ labels: ['unrelated-label'],
3879
+ defaultAgentName: 'default-agent',
3880
+ defaultLlmAgentName: null,
3881
+ labelsAsLlmAgentName: ['story'],
3882
+ });
3883
+ expect(selectedAgent).toBe('default-agent');
3884
+ });
3885
+
3886
+ it('ignores labels that are not listed in labelsAsLlmAgentName', async () => {
3887
+ const selectedAgent = await runWithIssueLabels({
3888
+ labels: ['untracked-label'],
3889
+ defaultAgentName: 'default-agent',
3890
+ defaultLlmAgentName: 'default-llm-agent',
3891
+ labelsAsLlmAgentName: ['story'],
3892
+ });
3893
+ expect(selectedAgent).toBe('default-llm-agent');
3894
+ });
3895
+
3896
+ it('ignores labels that are not listed in labelsAsLlmAgentName when other entries are present', async () => {
3897
+ const selectedAgent = await runWithIssueLabels({
3898
+ labels: ['untracked-label', 'another-untracked-label'],
3899
+ defaultAgentName: 'default-agent',
3900
+ defaultLlmAgentName: 'default-llm-agent',
3901
+ labelsAsLlmAgentName: ['story', 'story:body-condition'],
3902
+ });
3903
+ expect(selectedAgent).toBe('default-llm-agent');
3904
+ });
3905
+
3906
+ it('does not affect selection when labelsAsLlmAgentName is null and only category: label is present', async () => {
3907
+ const selectedAgent = await runWithIssueLabels({
3908
+ labels: ['category:impl'],
3909
+ defaultAgentName: 'default-agent',
3910
+ defaultLlmAgentName: 'default-llm-agent',
3911
+ labelsAsLlmAgentName: null,
3912
+ });
3913
+ expect(selectedAgent).toBe('impl');
3914
+ });
3915
+ });
3718
3916
  });
3719
3917
 
3720
3918
  describe('StartPreparationUseCase.buildRotationOrder', () => {
@@ -217,6 +217,7 @@ export class StartPreparationUseCase {
217
217
  allowedIssueAuthors: string[] | null;
218
218
  codexHomeCandidates: string[] | null;
219
219
  allowIssueCacheMinutes: number;
220
+ labelsAsLlmAgentName: string[] | null;
220
221
  }): Promise<{ rotationOrder: RotationOrderEntry[] | null }> => {
221
222
  const tokenUsages =
222
223
  await this.claudeTokenUsageRepository.getAvailableTokenUsages();
@@ -321,11 +322,20 @@ export class StartPreparationUseCase {
321
322
  ) {
322
323
  continue;
323
324
  }
325
+ const mappedAgentFromLabel =
326
+ params.labelsAsLlmAgentName !== null
327
+ ? issue.labels.find((label: string) =>
328
+ params.labelsAsLlmAgentName !== null
329
+ ? params.labelsAsLlmAgentName.includes(label)
330
+ : false,
331
+ )
332
+ : undefined;
324
333
  const agent =
325
334
  issue.labels
326
335
  .find((label: string) => label.startsWith('llm-agent:'))
327
336
  ?.replace('llm-agent:', '')
328
337
  .trim() ||
338
+ mappedAgentFromLabel ||
329
339
  issue.labels
330
340
  .find((label: string) => label.startsWith('category:'))
331
341
  ?.replace('category:', '')
@@ -15,6 +15,7 @@ export type ConfigFile = {
15
15
  claudeCodeOauthTokenListJsonPath?: string;
16
16
  awLogDirectoryPath?: string;
17
17
  awLogStaleThresholdMinutes?: number;
18
+ labelsAsLlmAgentName?: string[];
18
19
  };
19
20
  export declare const isRecord: (value: unknown) => value is Record<string, unknown>;
20
21
  export declare const loadConfigFile: (configFilePath: string) => ConfigFile;
@@ -1 +1 @@
1
- {"version":3,"file":"projectConfig.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/projectConfig.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,iCAAiC,CAAC,EAAE,MAAM,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,gCAAgC,CAAC,EAAE,MAAM,CAAC;IAC1C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC,CAAC;AAoCF,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CACH,CAAC;AAEvE,eAAO,MAAM,cAAc,GAAI,gBAAgB,MAAM,KAAG,UAkDvD,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,QAAQ,MAAM,KAAG,UAsDzD,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,YAAY,UAAU,EACtB,cAAc,UAAU,EACxB,iBAAiB,UAAU,KAC1B,UA2DD,CAAC;AAkBH,eAAO,MAAM,kBAAkB,GAC7B,YAAY,MAAM,EAClB,OAAO,MAAM,KACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CA0DvB,CAAC"}
1
+ {"version":3,"file":"projectConfig.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/cli/projectConfig.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,iCAAiC,CAAC,EAAE,MAAM,CAAC;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,mBAAmB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC/B,gCAAgC,CAAC,EAAE,MAAM,CAAC;IAC1C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0BAA0B,CAAC,EAAE,MAAM,CAAC;IACpC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;CACjC,CAAC;AAoCF,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CACH,CAAC;AAEvE,eAAO,MAAM,cAAc,GAAI,gBAAgB,MAAM,KAAG,UAmDvD,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAI,QAAQ,MAAM,KAAG,UAsDzD,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,YAAY,UAAU,EACtB,cAAc,UAAU,EACxB,iBAAiB,UAAU,KAC1B,UA+DD,CAAC;AAkBH,eAAO,MAAM,kBAAkB,GAC7B,YAAY,MAAM,EAClB,OAAO,MAAM,KACZ,OAAO,CAAC,MAAM,GAAG,IAAI,CA0DvB,CAAC"}
@@ -77,6 +77,7 @@ export declare class HandleScheduledEventUseCase {
77
77
  awLogDirectoryPath?: string;
78
78
  awLogStaleThresholdMinutes?: number;
79
79
  awaitingQualityCheckStatus?: string | null;
80
+ labelsAsLlmAgentName?: string[] | null;
80
81
  } | null;
81
82
  notifyFinishedPreparation?: {
82
83
  awaitingQualityCheckStatusName?: string | null;