github-issue-tower-defence-management 1.46.0 → 1.48.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.
- package/CHANGELOG.md +24 -0
- package/README.md +70 -25
- package/bin/adapter/entry-points/cli/index.js +2 -104
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/cli/projectConfig.js +0 -15
- package/bin/adapter/entry-points/cli/projectConfig.js.map +1 -1
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +28 -62
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/situationFileWriter.js +98 -0
- package/bin/adapter/entry-points/handlers/situationFileWriter.js.map +1 -0
- package/bin/adapter/repositories/GraphqlProjectRepository.js +37 -0
- package/bin/adapter/repositories/GraphqlProjectRepository.js.map +1 -1
- package/bin/domain/entities/WorkflowStatus.js +36 -0
- package/bin/domain/entities/WorkflowStatus.js.map +1 -0
- package/bin/domain/usecases/AnalyzeStoriesUseCase.js +2 -1
- package/bin/domain/usecases/AnalyzeStoriesUseCase.js.map +1 -1
- package/bin/domain/usecases/ChangeStatusByStoryColorUseCase.js +4 -3
- package/bin/domain/usecases/ChangeStatusByStoryColorUseCase.js.map +1 -1
- package/bin/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.js +2 -1
- package/bin/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.js.map +1 -1
- package/bin/domain/usecases/CreateEstimationIssueUseCase.js +2 -1
- package/bin/domain/usecases/CreateEstimationIssueUseCase.js.map +1 -1
- package/bin/domain/usecases/CreateNewStoryByLabelUseCase.js.map +1 -1
- package/bin/domain/usecases/HandleScheduledEventUseCase.js +9 -17
- package/bin/domain/usecases/HandleScheduledEventUseCase.js.map +1 -1
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js +13 -15
- package/bin/domain/usecases/NotifyFinishedIssuePreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js +4 -5
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/SetupTowerDefenceProjectUseCase.js +47 -0
- package/bin/domain/usecases/SetupTowerDefenceProjectUseCase.js.map +1 -0
- package/bin/domain/usecases/StartPreparationUseCase.js +7 -8
- package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/UpdateIssueStatusByLabelUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.test.ts +8 -258
- package/src/adapter/entry-points/cli/index.ts +6 -106
- package/src/adapter/entry-points/cli/projectConfig.ts +0 -33
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.test.ts +24 -58
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +36 -41
- package/src/adapter/entry-points/handlers/situationFileWriter.test.ts +417 -0
- package/src/adapter/entry-points/handlers/situationFileWriter.ts +168 -0
- package/src/adapter/repositories/GraphqlProjectRepository.ts +55 -1
- package/src/domain/entities/WorkflowStatus.ts +41 -0
- package/src/domain/usecases/AnalyzeStoriesUseCase.ts +2 -2
- package/src/domain/usecases/ChangeStatusByStoryColorUseCase.test.ts +5 -10
- package/src/domain/usecases/ChangeStatusByStoryColorUseCase.ts +4 -4
- package/src/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.test.ts +0 -11
- package/src/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.ts +2 -2
- package/src/domain/usecases/CreateEstimationIssueUseCase.ts +2 -2
- package/src/domain/usecases/CreateNewStoryByLabelUseCase.test.ts +0 -4
- package/src/domain/usecases/CreateNewStoryByLabelUseCase.ts +0 -1
- package/src/domain/usecases/HandleScheduledEventUseCase.test.ts +4 -41
- package/src/domain/usecases/HandleScheduledEventUseCase.ts +9 -27
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +0 -202
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.ts +18 -31
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +13 -101
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +10 -10
- package/src/domain/usecases/SetupTowerDefenceProjectUseCase.test.ts +187 -0
- package/src/domain/usecases/SetupTowerDefenceProjectUseCase.ts +69 -0
- package/src/domain/usecases/StartPreparationUseCase.test.ts +1 -151
- package/src/domain/usecases/StartPreparationUseCase.ts +11 -20
- package/src/domain/usecases/UpdateIssueStatusByLabelUseCase.test.ts +2 -47
- package/src/domain/usecases/UpdateIssueStatusByLabelUseCase.ts +1 -5
- package/src/domain/usecases/adapter-interfaces/ProjectRepository.ts +6 -1
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/entry-points/cli/projectConfig.d.ts +0 -3
- package/types/adapter/entry-points/cli/projectConfig.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/situationFileWriter.d.ts +30 -0
- package/types/adapter/entry-points/handlers/situationFileWriter.d.ts.map +1 -0
- package/types/adapter/repositories/GraphqlProjectRepository.d.ts +4 -1
- package/types/adapter/repositories/GraphqlProjectRepository.d.ts.map +1 -1
- package/types/domain/entities/WorkflowStatus.d.ts +13 -0
- package/types/domain/entities/WorkflowStatus.d.ts.map +1 -0
- package/types/domain/usecases/AnalyzeStoriesUseCase.d.ts +0 -1
- package/types/domain/usecases/AnalyzeStoriesUseCase.d.ts.map +1 -1
- package/types/domain/usecases/ChangeStatusByStoryColorUseCase.d.ts +0 -1
- package/types/domain/usecases/ChangeStatusByStoryColorUseCase.d.ts.map +1 -1
- package/types/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.d.ts +0 -1
- package/types/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.d.ts.map +1 -1
- package/types/domain/usecases/CreateEstimationIssueUseCase.d.ts +0 -1
- package/types/domain/usecases/CreateEstimationIssueUseCase.d.ts.map +1 -1
- package/types/domain/usecases/CreateNewStoryByLabelUseCase.d.ts +0 -1
- package/types/domain/usecases/CreateNewStoryByLabelUseCase.d.ts.map +1 -1
- package/types/domain/usecases/HandleScheduledEventUseCase.d.ts +3 -8
- package/types/domain/usecases/HandleScheduledEventUseCase.d.ts.map +1 -1
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts +1 -4
- package/types/domain/usecases/NotifyFinishedIssuePreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/RevertOrphanedPreparationUseCase.d.ts +0 -3
- package/types/domain/usecases/RevertOrphanedPreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/SetupTowerDefenceProjectUseCase.d.ts +10 -0
- package/types/domain/usecases/SetupTowerDefenceProjectUseCase.d.ts.map +1 -0
- package/types/domain/usecases/StartPreparationUseCase.d.ts +1 -3
- package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/UpdateIssueStatusByLabelUseCase.d.ts +0 -1
- package/types/domain/usecases/UpdateIssueStatusByLabelUseCase.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/ProjectRepository.d.ts +3 -1
- package/types/domain/usecases/adapter-interfaces/ProjectRepository.d.ts.map +1 -1
|
@@ -66,10 +66,7 @@ describe('CLI', () => {
|
|
|
66
66
|
|
|
67
67
|
const defaultConfig = {
|
|
68
68
|
projectUrl: 'https://github.com/orgs/test/projects/1',
|
|
69
|
-
awaitingWorkspaceStatus: 'Awaiting',
|
|
70
|
-
preparationStatus: 'Preparing',
|
|
71
69
|
defaultAgentName: 'agent1',
|
|
72
|
-
awaitingQualityCheckStatus: 'Awaiting QC',
|
|
73
70
|
projectName: 'test-project',
|
|
74
71
|
};
|
|
75
72
|
|
|
@@ -128,8 +125,6 @@ describe('CLI', () => {
|
|
|
128
125
|
it('should load config from YAML file', () => {
|
|
129
126
|
const config = {
|
|
130
127
|
projectUrl: 'https://github.com/orgs/test/projects/1',
|
|
131
|
-
awaitingWorkspaceStatus: 'Awaiting',
|
|
132
|
-
preparationStatus: 'Preparing',
|
|
133
128
|
defaultAgentName: 'agent1',
|
|
134
129
|
defaultLlmModelName: 'claude-opus-4-5',
|
|
135
130
|
defaultLlmAgentName: 'aw',
|
|
@@ -137,7 +132,6 @@ describe('CLI', () => {
|
|
|
137
132
|
allowIssueCacheMinutes: 5,
|
|
138
133
|
utilizationPercentageThreshold: 80,
|
|
139
134
|
allowedIssueAuthors: 'user1,user2',
|
|
140
|
-
awaitingQualityCheckStatus: 'Awaiting QC',
|
|
141
135
|
thresholdForAutoReject: 5,
|
|
142
136
|
workflowBlockerResolvedWebhookUrl: 'https://example.com/webhook',
|
|
143
137
|
projectName: 'test-project',
|
|
@@ -197,14 +191,14 @@ describe('CLI', () => {
|
|
|
197
191
|
it('should ignore non-string values for string fields', () => {
|
|
198
192
|
const config = {
|
|
199
193
|
projectUrl: 123,
|
|
200
|
-
|
|
194
|
+
defaultAgentName: true,
|
|
201
195
|
};
|
|
202
196
|
writeConfig(config);
|
|
203
197
|
|
|
204
198
|
const result = loadConfigFile(configFilePath);
|
|
205
199
|
|
|
206
200
|
expect(result.projectUrl).toBeUndefined();
|
|
207
|
-
expect(result.
|
|
201
|
+
expect(result.defaultAgentName).toBeUndefined();
|
|
208
202
|
});
|
|
209
203
|
|
|
210
204
|
it('should ignore non-number values for number fields', () => {
|
|
@@ -263,16 +257,14 @@ describe('CLI', () => {
|
|
|
263
257
|
Some description
|
|
264
258
|
<details>
|
|
265
259
|
<summary>config</summary>
|
|
266
|
-
awaitingWorkspaceStatus: 'Custom Awaiting'
|
|
267
|
-
preparationStatus: 'Custom Preparing'
|
|
268
260
|
defaultAgentName: 'readme-agent'
|
|
261
|
+
defaultLlmModelName: 'claude-opus-4-5'
|
|
269
262
|
</details>`;
|
|
270
263
|
|
|
271
264
|
const result = parseProjectReadmeConfig(readme);
|
|
272
265
|
|
|
273
|
-
expect(result.awaitingWorkspaceStatus).toBe('Custom Awaiting');
|
|
274
|
-
expect(result.preparationStatus).toBe('Custom Preparing');
|
|
275
266
|
expect(result.defaultAgentName).toBe('readme-agent');
|
|
267
|
+
expect(result.defaultLlmModelName).toBe('claude-opus-4-5');
|
|
276
268
|
});
|
|
277
269
|
|
|
278
270
|
it('should return empty config when no details/summary section exists', () => {
|
|
@@ -506,8 +498,6 @@ codexHomeCandidates:
|
|
|
506
498
|
expect(mockRun).toHaveBeenCalledTimes(1);
|
|
507
499
|
expect(mockRun).toHaveBeenCalledWith({
|
|
508
500
|
projectUrl: 'https://github.com/orgs/test/projects/1',
|
|
509
|
-
awaitingWorkspaceStatus: 'Awaiting',
|
|
510
|
-
preparationStatus: 'Preparing',
|
|
511
501
|
defaultAgentName: 'agent1',
|
|
512
502
|
defaultLlmModelName: null,
|
|
513
503
|
defaultLlmAgentName: null,
|
|
@@ -548,8 +538,6 @@ codexHomeCandidates:
|
|
|
548
538
|
expect(mockRun).toHaveBeenCalledTimes(1);
|
|
549
539
|
expect(mockRun).toHaveBeenCalledWith({
|
|
550
540
|
projectUrl: 'https://github.com/orgs/override/projects/2',
|
|
551
|
-
awaitingWorkspaceStatus: 'Awaiting',
|
|
552
|
-
preparationStatus: 'Preparing',
|
|
553
541
|
defaultAgentName: 'override-agent',
|
|
554
542
|
defaultLlmModelName: null,
|
|
555
543
|
defaultLlmAgentName: null,
|
|
@@ -750,8 +738,6 @@ codexHomeCandidates:
|
|
|
750
738
|
|
|
751
739
|
it('should exit with error when projectUrl is missing from both CLI and config', async () => {
|
|
752
740
|
const configWithoutProjectUrl = {
|
|
753
|
-
awaitingWorkspaceStatus: 'Awaiting',
|
|
754
|
-
preparationStatus: 'Preparing',
|
|
755
741
|
defaultAgentName: 'agent1',
|
|
756
742
|
};
|
|
757
743
|
writeConfig(configWithoutProjectUrl);
|
|
@@ -782,79 +768,9 @@ codexHomeCandidates:
|
|
|
782
768
|
processExitSpy.mockRestore();
|
|
783
769
|
});
|
|
784
770
|
|
|
785
|
-
it('should exit with error when awaitingWorkspaceStatus is missing', async () => {
|
|
786
|
-
const configMissing = {
|
|
787
|
-
projectUrl: 'https://github.com/orgs/test/projects/1',
|
|
788
|
-
preparationStatus: 'Preparing',
|
|
789
|
-
defaultAgentName: 'agent1',
|
|
790
|
-
};
|
|
791
|
-
writeConfig(configMissing);
|
|
792
|
-
|
|
793
|
-
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
794
|
-
const processExitSpy = jest
|
|
795
|
-
.spyOn(process, 'exit')
|
|
796
|
-
.mockImplementation(() => {
|
|
797
|
-
throw new Error('process.exit called');
|
|
798
|
-
});
|
|
799
|
-
|
|
800
|
-
await expect(
|
|
801
|
-
program.parseAsync([
|
|
802
|
-
'node',
|
|
803
|
-
'test',
|
|
804
|
-
'startDaemon',
|
|
805
|
-
'--configFilePath',
|
|
806
|
-
configFilePath,
|
|
807
|
-
]),
|
|
808
|
-
).rejects.toThrow('process.exit called');
|
|
809
|
-
|
|
810
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
811
|
-
'awaitingWorkspaceStatus is required. Provide via --awaitingWorkspaceStatus, config file, or project README.',
|
|
812
|
-
);
|
|
813
|
-
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
814
|
-
|
|
815
|
-
consoleErrorSpy.mockRestore();
|
|
816
|
-
processExitSpy.mockRestore();
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
it('should exit with error when preparationStatus is missing', async () => {
|
|
820
|
-
const configMissing = {
|
|
821
|
-
projectUrl: 'https://github.com/orgs/test/projects/1',
|
|
822
|
-
awaitingWorkspaceStatus: 'Awaiting',
|
|
823
|
-
defaultAgentName: 'agent1',
|
|
824
|
-
};
|
|
825
|
-
writeConfig(configMissing);
|
|
826
|
-
|
|
827
|
-
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
828
|
-
const processExitSpy = jest
|
|
829
|
-
.spyOn(process, 'exit')
|
|
830
|
-
.mockImplementation(() => {
|
|
831
|
-
throw new Error('process.exit called');
|
|
832
|
-
});
|
|
833
|
-
|
|
834
|
-
await expect(
|
|
835
|
-
program.parseAsync([
|
|
836
|
-
'node',
|
|
837
|
-
'test',
|
|
838
|
-
'startDaemon',
|
|
839
|
-
'--configFilePath',
|
|
840
|
-
configFilePath,
|
|
841
|
-
]),
|
|
842
|
-
).rejects.toThrow('process.exit called');
|
|
843
|
-
|
|
844
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
845
|
-
'preparationStatus is required. Provide via --preparationStatus, config file, or project README.',
|
|
846
|
-
);
|
|
847
|
-
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
848
|
-
|
|
849
|
-
consoleErrorSpy.mockRestore();
|
|
850
|
-
processExitSpy.mockRestore();
|
|
851
|
-
});
|
|
852
|
-
|
|
853
771
|
it('should exit with error when defaultAgentName is missing', async () => {
|
|
854
772
|
const configMissing = {
|
|
855
773
|
projectUrl: 'https://github.com/orgs/test/projects/1',
|
|
856
|
-
awaitingWorkspaceStatus: 'Awaiting',
|
|
857
|
-
preparationStatus: 'Preparing',
|
|
858
774
|
};
|
|
859
775
|
writeConfig(configMissing);
|
|
860
776
|
|
|
@@ -1068,52 +984,6 @@ codexHomeCandidates:
|
|
|
1068
984
|
}),
|
|
1069
985
|
);
|
|
1070
986
|
});
|
|
1071
|
-
|
|
1072
|
-
it('should write runtimeConfig-{projectId}.json atomically after useCase run', async () => {
|
|
1073
|
-
const configWithNumericValues = {
|
|
1074
|
-
...defaultConfig,
|
|
1075
|
-
maximumPreparingIssuesCount: 10,
|
|
1076
|
-
utilizationPercentageThreshold: 97,
|
|
1077
|
-
allowIssueCacheMinutes: 5,
|
|
1078
|
-
thresholdForAutoReject: 30,
|
|
1079
|
-
};
|
|
1080
|
-
writeConfig(configWithNumericValues);
|
|
1081
|
-
|
|
1082
|
-
const mockRun = jest.fn().mockResolvedValue(undefined);
|
|
1083
|
-
const MockedStartPreparationUseCase = jest.mocked(
|
|
1084
|
-
StartPreparationUseCase,
|
|
1085
|
-
);
|
|
1086
|
-
MockedStartPreparationUseCase.mockImplementation(function (
|
|
1087
|
-
this: StartPreparationUseCase,
|
|
1088
|
-
) {
|
|
1089
|
-
this.run = mockRun;
|
|
1090
|
-
return this;
|
|
1091
|
-
});
|
|
1092
|
-
|
|
1093
|
-
await program.parseAsync([
|
|
1094
|
-
'node',
|
|
1095
|
-
'test',
|
|
1096
|
-
'startDaemon',
|
|
1097
|
-
'--configFilePath',
|
|
1098
|
-
configFilePath,
|
|
1099
|
-
]);
|
|
1100
|
-
|
|
1101
|
-
const cacheDir = path.join(process.cwd(), 'tmp/cache/test-project');
|
|
1102
|
-
const runtimeConfigPath = path.join(
|
|
1103
|
-
cacheDir,
|
|
1104
|
-
'runtimeConfig-PVT_kwHOtest456.json',
|
|
1105
|
-
);
|
|
1106
|
-
expect(fs.existsSync(runtimeConfigPath)).toBe(true);
|
|
1107
|
-
const written: unknown = JSON.parse(
|
|
1108
|
-
fs.readFileSync(runtimeConfigPath, 'utf-8'),
|
|
1109
|
-
);
|
|
1110
|
-
expect(written).toMatchObject({
|
|
1111
|
-
maximumPreparingIssuesCount: 10,
|
|
1112
|
-
utilizationPercentageThreshold: 97,
|
|
1113
|
-
allowIssueCacheMinutes: 5,
|
|
1114
|
-
thresholdForAutoReject: 30,
|
|
1115
|
-
});
|
|
1116
|
-
});
|
|
1117
987
|
});
|
|
1118
988
|
|
|
1119
989
|
describe('notifyFinishedIssuePreparation', () => {
|
|
@@ -1144,9 +1014,6 @@ codexHomeCandidates:
|
|
|
1144
1014
|
expect(mockRun).toHaveBeenCalledWith({
|
|
1145
1015
|
projectUrl: 'https://github.com/orgs/test/projects/1',
|
|
1146
1016
|
issueUrl: 'https://github.com/test/repo/issues/1',
|
|
1147
|
-
preparationStatus: 'Preparing',
|
|
1148
|
-
awaitingWorkspaceStatus: 'Awaiting',
|
|
1149
|
-
awaitingQualityCheckStatus: 'Awaiting QC',
|
|
1150
1017
|
thresholdForAutoReject: 3,
|
|
1151
1018
|
workflowBlockerResolvedWebhookUrl: null,
|
|
1152
1019
|
});
|
|
@@ -1175,16 +1042,11 @@ codexHomeCandidates:
|
|
|
1175
1042
|
'https://github.com/test/repo/issues/1',
|
|
1176
1043
|
'--projectUrl',
|
|
1177
1044
|
'https://github.com/orgs/override/projects/2',
|
|
1178
|
-
'--awaitingQualityCheckStatus',
|
|
1179
|
-
'Override QC',
|
|
1180
1045
|
]);
|
|
1181
1046
|
|
|
1182
1047
|
expect(mockRun).toHaveBeenCalledWith({
|
|
1183
1048
|
projectUrl: 'https://github.com/orgs/override/projects/2',
|
|
1184
1049
|
issueUrl: 'https://github.com/test/repo/issues/1',
|
|
1185
|
-
preparationStatus: 'Preparing',
|
|
1186
|
-
awaitingWorkspaceStatus: 'Awaiting',
|
|
1187
|
-
awaitingQualityCheckStatus: 'Override QC',
|
|
1188
1050
|
thresholdForAutoReject: 3,
|
|
1189
1051
|
workflowBlockerResolvedWebhookUrl: null,
|
|
1190
1052
|
});
|
|
@@ -1320,11 +1182,7 @@ codexHomeCandidates:
|
|
|
1320
1182
|
});
|
|
1321
1183
|
|
|
1322
1184
|
it('should exit with error when projectUrl is missing', async () => {
|
|
1323
|
-
const configMissing = {
|
|
1324
|
-
preparationStatus: 'Preparing',
|
|
1325
|
-
awaitingWorkspaceStatus: 'Awaiting',
|
|
1326
|
-
awaitingQualityCheckStatus: 'Awaiting QC',
|
|
1327
|
-
};
|
|
1185
|
+
const configMissing = {};
|
|
1328
1186
|
writeConfig(configMissing);
|
|
1329
1187
|
|
|
1330
1188
|
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
@@ -1355,114 +1213,6 @@ codexHomeCandidates:
|
|
|
1355
1213
|
processExitSpy.mockRestore();
|
|
1356
1214
|
});
|
|
1357
1215
|
|
|
1358
|
-
it('should exit with error when preparationStatus is missing', async () => {
|
|
1359
|
-
const configMissing = {
|
|
1360
|
-
projectUrl: 'https://github.com/orgs/test/projects/1',
|
|
1361
|
-
awaitingWorkspaceStatus: 'Awaiting',
|
|
1362
|
-
awaitingQualityCheckStatus: 'Awaiting QC',
|
|
1363
|
-
};
|
|
1364
|
-
writeConfig(configMissing);
|
|
1365
|
-
|
|
1366
|
-
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
1367
|
-
const processExitSpy = jest
|
|
1368
|
-
.spyOn(process, 'exit')
|
|
1369
|
-
.mockImplementation(() => {
|
|
1370
|
-
throw new Error('process.exit called');
|
|
1371
|
-
});
|
|
1372
|
-
|
|
1373
|
-
await expect(
|
|
1374
|
-
program.parseAsync([
|
|
1375
|
-
'node',
|
|
1376
|
-
'test',
|
|
1377
|
-
'notifyFinishedIssuePreparation',
|
|
1378
|
-
'--configFilePath',
|
|
1379
|
-
configFilePath,
|
|
1380
|
-
'--issueUrl',
|
|
1381
|
-
'https://github.com/test/repo/issues/1',
|
|
1382
|
-
]),
|
|
1383
|
-
).rejects.toThrow('process.exit called');
|
|
1384
|
-
|
|
1385
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
1386
|
-
'preparationStatus is required. Provide via --preparationStatus, config file, or project README.',
|
|
1387
|
-
);
|
|
1388
|
-
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
1389
|
-
|
|
1390
|
-
consoleErrorSpy.mockRestore();
|
|
1391
|
-
processExitSpy.mockRestore();
|
|
1392
|
-
});
|
|
1393
|
-
|
|
1394
|
-
it('should exit with error when awaitingWorkspaceStatus is missing', async () => {
|
|
1395
|
-
const configMissing = {
|
|
1396
|
-
projectUrl: 'https://github.com/orgs/test/projects/1',
|
|
1397
|
-
preparationStatus: 'Preparing',
|
|
1398
|
-
awaitingQualityCheckStatus: 'Awaiting QC',
|
|
1399
|
-
};
|
|
1400
|
-
writeConfig(configMissing);
|
|
1401
|
-
|
|
1402
|
-
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
1403
|
-
const processExitSpy = jest
|
|
1404
|
-
.spyOn(process, 'exit')
|
|
1405
|
-
.mockImplementation(() => {
|
|
1406
|
-
throw new Error('process.exit called');
|
|
1407
|
-
});
|
|
1408
|
-
|
|
1409
|
-
await expect(
|
|
1410
|
-
program.parseAsync([
|
|
1411
|
-
'node',
|
|
1412
|
-
'test',
|
|
1413
|
-
'notifyFinishedIssuePreparation',
|
|
1414
|
-
'--configFilePath',
|
|
1415
|
-
configFilePath,
|
|
1416
|
-
'--issueUrl',
|
|
1417
|
-
'https://github.com/test/repo/issues/1',
|
|
1418
|
-
]),
|
|
1419
|
-
).rejects.toThrow('process.exit called');
|
|
1420
|
-
|
|
1421
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
1422
|
-
'awaitingWorkspaceStatus is required. Provide via --awaitingWorkspaceStatus, config file, or project README.',
|
|
1423
|
-
);
|
|
1424
|
-
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
1425
|
-
|
|
1426
|
-
consoleErrorSpy.mockRestore();
|
|
1427
|
-
processExitSpy.mockRestore();
|
|
1428
|
-
});
|
|
1429
|
-
|
|
1430
|
-
it('should exit with error when awaitingQualityCheckStatus is missing', async () => {
|
|
1431
|
-
const configMissing = {
|
|
1432
|
-
projectUrl: 'https://github.com/orgs/test/projects/1',
|
|
1433
|
-
preparationStatus: 'Preparing',
|
|
1434
|
-
awaitingWorkspaceStatus: 'Awaiting',
|
|
1435
|
-
};
|
|
1436
|
-
writeConfig(configMissing);
|
|
1437
|
-
|
|
1438
|
-
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
1439
|
-
const processExitSpy = jest
|
|
1440
|
-
.spyOn(process, 'exit')
|
|
1441
|
-
.mockImplementation(() => {
|
|
1442
|
-
throw new Error('process.exit called');
|
|
1443
|
-
});
|
|
1444
|
-
|
|
1445
|
-
await expect(
|
|
1446
|
-
program.parseAsync([
|
|
1447
|
-
'node',
|
|
1448
|
-
'test',
|
|
1449
|
-
'notifyFinishedIssuePreparation',
|
|
1450
|
-
'--configFilePath',
|
|
1451
|
-
configFilePath,
|
|
1452
|
-
'--issueUrl',
|
|
1453
|
-
'https://github.com/test/repo/issues/1',
|
|
1454
|
-
]),
|
|
1455
|
-
).rejects.toThrow('process.exit called');
|
|
1456
|
-
|
|
1457
|
-
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
1458
|
-
'awaitingQualityCheckStatus is required. Provide via --awaitingQualityCheckStatus, config file, or project README.',
|
|
1459
|
-
);
|
|
1460
|
-
expect(processExitSpy).toHaveBeenCalledWith(1);
|
|
1461
|
-
|
|
1462
|
-
consoleErrorSpy.mockRestore();
|
|
1463
|
-
processExitSpy.mockRestore();
|
|
1464
|
-
});
|
|
1465
|
-
|
|
1466
1216
|
it('should pass workflowBlockerResolvedWebhookUrl from config file', async () => {
|
|
1467
1217
|
const configWithWebhook = {
|
|
1468
1218
|
...defaultConfig,
|
|
@@ -1501,12 +1251,12 @@ codexHomeCandidates:
|
|
|
1501
1251
|
);
|
|
1502
1252
|
});
|
|
1503
1253
|
|
|
1504
|
-
it('should apply README config overrides', async () => {
|
|
1254
|
+
it('should apply README config overrides for thresholdForAutoReject', async () => {
|
|
1505
1255
|
const readmeContent = [
|
|
1506
1256
|
'# Project',
|
|
1507
1257
|
'<details>',
|
|
1508
1258
|
'<summary>config</summary>',
|
|
1509
|
-
|
|
1259
|
+
'thresholdForAutoReject: 9',
|
|
1510
1260
|
'</details>',
|
|
1511
1261
|
].join('\n');
|
|
1512
1262
|
mockFetchReturningReadme(readmeContent);
|
|
@@ -1535,7 +1285,7 @@ codexHomeCandidates:
|
|
|
1535
1285
|
|
|
1536
1286
|
expect(mockRun).toHaveBeenCalledWith(
|
|
1537
1287
|
expect.objectContaining({
|
|
1538
|
-
|
|
1288
|
+
thresholdForAutoReject: 9,
|
|
1539
1289
|
}),
|
|
1540
1290
|
);
|
|
1541
1291
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import * as fs from 'fs';
|
|
3
2
|
import { Command } from 'commander';
|
|
4
3
|
import { HandleScheduledEventUseCaseHandler } from '../handlers/HandleScheduledEventUseCaseHandler';
|
|
5
4
|
export {
|
|
@@ -31,12 +30,9 @@ import { OauthAPIProxyClaudeRepository } from '../../repositories/OauthAPIProxyC
|
|
|
31
30
|
import { GitHubIssueCommentRepository } from '../../repositories/GitHubIssueCommentRepository';
|
|
32
31
|
import { FetchWebhookRepository } from '../../repositories/FetchWebhookRepository';
|
|
33
32
|
import { RevertOrphanedPreparationUseCase } from '../../../domain/usecases/RevertOrphanedPreparationUseCase';
|
|
34
|
-
import { Project } from '../../../domain/entities/Project';
|
|
35
33
|
|
|
36
34
|
type StartDaemonOptions = {
|
|
37
35
|
projectUrl?: string;
|
|
38
|
-
awaitingWorkspaceStatus?: string;
|
|
39
|
-
preparationStatus?: string;
|
|
40
36
|
defaultAgentName?: string;
|
|
41
37
|
defaultLlmModelName?: string;
|
|
42
38
|
defaultLlmAgentName?: string;
|
|
@@ -51,9 +47,6 @@ type StartDaemonOptions = {
|
|
|
51
47
|
type NotifyFinishedOptions = {
|
|
52
48
|
issueUrl: string;
|
|
53
49
|
projectUrl?: string;
|
|
54
|
-
preparationStatus?: string;
|
|
55
|
-
awaitingWorkspaceStatus?: string;
|
|
56
|
-
awaitingQualityCheckStatus?: string;
|
|
57
50
|
thresholdForAutoReject?: string;
|
|
58
51
|
workflowBlockerResolvedWebhookUrl?: string;
|
|
59
52
|
configFilePath: string;
|
|
@@ -115,11 +108,6 @@ program
|
|
|
115
108
|
'Path to config file for tower defence management',
|
|
116
109
|
)
|
|
117
110
|
.option('--projectUrl <url>', 'GitHub project URL')
|
|
118
|
-
.option(
|
|
119
|
-
'--awaitingWorkspaceStatus <status>',
|
|
120
|
-
'Status for issues awaiting workspace',
|
|
121
|
-
)
|
|
122
|
-
.option('--preparationStatus <status>', 'Status for issues in preparation')
|
|
123
111
|
.option('--defaultAgentName <name>', 'Default agent name')
|
|
124
112
|
.option('--defaultLlmModelName <name>', 'Default LLM model name')
|
|
125
113
|
.option('--defaultLlmAgentName <name>', 'Default LLM agent name')
|
|
@@ -154,8 +142,6 @@ program
|
|
|
154
142
|
|
|
155
143
|
const cliOverrides: ConfigFile = {
|
|
156
144
|
projectUrl: options.projectUrl,
|
|
157
|
-
awaitingWorkspaceStatus: options.awaitingWorkspaceStatus,
|
|
158
|
-
preparationStatus: options.preparationStatus,
|
|
159
145
|
defaultAgentName: options.defaultAgentName,
|
|
160
146
|
defaultLlmModelName: options.defaultLlmModelName,
|
|
161
147
|
defaultLlmAgentName: options.defaultLlmAgentName,
|
|
@@ -190,8 +176,6 @@ program
|
|
|
190
176
|
);
|
|
191
177
|
|
|
192
178
|
const projectUrl = config.projectUrl;
|
|
193
|
-
const awaitingWorkspaceStatus = config.awaitingWorkspaceStatus;
|
|
194
|
-
const preparationStatus = config.preparationStatus;
|
|
195
179
|
const defaultAgentName = config.defaultAgentName;
|
|
196
180
|
|
|
197
181
|
if (!projectUrl) {
|
|
@@ -200,18 +184,6 @@ program
|
|
|
200
184
|
);
|
|
201
185
|
process.exit(1);
|
|
202
186
|
}
|
|
203
|
-
if (!awaitingWorkspaceStatus) {
|
|
204
|
-
console.error(
|
|
205
|
-
'awaitingWorkspaceStatus is required. Provide via --awaitingWorkspaceStatus, config file, or project README.',
|
|
206
|
-
);
|
|
207
|
-
process.exit(1);
|
|
208
|
-
}
|
|
209
|
-
if (!preparationStatus) {
|
|
210
|
-
console.error(
|
|
211
|
-
'preparationStatus is required. Provide via --preparationStatus, config file, or project README.',
|
|
212
|
-
);
|
|
213
|
-
process.exit(1);
|
|
214
|
-
}
|
|
215
187
|
if (!defaultAgentName) {
|
|
216
188
|
console.error(
|
|
217
189
|
'defaultAgentName is required. Provide via --defaultAgentName, config file, or project README.',
|
|
@@ -254,16 +226,9 @@ program
|
|
|
254
226
|
cachePath,
|
|
255
227
|
token,
|
|
256
228
|
);
|
|
257
|
-
const projectRepository =
|
|
258
|
-
...
|
|
259
|
-
|
|
260
|
-
prepareStatus: async (
|
|
261
|
-
_name: string,
|
|
262
|
-
project: Project,
|
|
263
|
-
): Promise<Project> => {
|
|
264
|
-
return project;
|
|
265
|
-
},
|
|
266
|
-
};
|
|
229
|
+
const projectRepository = new GraphqlProjectRepository(
|
|
230
|
+
...githubRepositoryParams,
|
|
231
|
+
);
|
|
267
232
|
const apiV3IssueRepository = new ApiV3IssueRepository(
|
|
268
233
|
...githubRepositoryParams,
|
|
269
234
|
);
|
|
@@ -297,9 +262,6 @@ program
|
|
|
297
262
|
);
|
|
298
263
|
await revertUseCase.run({
|
|
299
264
|
projectUrl,
|
|
300
|
-
preparationStatus,
|
|
301
|
-
awaitingWorkspaceStatus,
|
|
302
|
-
awaitingQualityCheckStatus: config.awaitingQualityCheckStatus,
|
|
303
265
|
allowIssueCacheMinutes,
|
|
304
266
|
preparationProcessCheckCommand,
|
|
305
267
|
awLogDirectoryPath: config.awLogDirectoryPath,
|
|
@@ -329,8 +291,6 @@ program
|
|
|
329
291
|
|
|
330
292
|
await useCase.run({
|
|
331
293
|
projectUrl,
|
|
332
|
-
awaitingWorkspaceStatus,
|
|
333
|
-
preparationStatus,
|
|
334
294
|
defaultAgentName,
|
|
335
295
|
defaultLlmModelName: config.defaultLlmModelName ?? null,
|
|
336
296
|
defaultLlmAgentName: config.defaultLlmAgentName ?? null,
|
|
@@ -342,23 +302,6 @@ program
|
|
|
342
302
|
codexHomeCandidates,
|
|
343
303
|
allowIssueCacheMinutes,
|
|
344
304
|
});
|
|
345
|
-
|
|
346
|
-
const projectId = await projectRepository.findProjectIdByUrl(projectUrl);
|
|
347
|
-
if (projectId) {
|
|
348
|
-
const runtimeConfig = {
|
|
349
|
-
resolvedAt: new Date().toISOString(),
|
|
350
|
-
maximumPreparingIssuesCount: maximumPreparingIssuesCount,
|
|
351
|
-
utilizationPercentageThreshold:
|
|
352
|
-
config.utilizationPercentageThreshold ?? 90,
|
|
353
|
-
allowIssueCacheMinutes: allowIssueCacheMinutes,
|
|
354
|
-
thresholdForAutoReject: config.thresholdForAutoReject ?? 3,
|
|
355
|
-
};
|
|
356
|
-
const finalPath = `${cachePath}/runtimeConfig-${projectId}.json`;
|
|
357
|
-
const tmpPath = `${finalPath}.tmp`;
|
|
358
|
-
fs.mkdirSync(cachePath, { recursive: true });
|
|
359
|
-
fs.writeFileSync(tmpPath, JSON.stringify(runtimeConfig));
|
|
360
|
-
fs.renameSync(tmpPath, finalPath);
|
|
361
|
-
}
|
|
362
305
|
});
|
|
363
306
|
|
|
364
307
|
program
|
|
@@ -370,15 +313,6 @@ program
|
|
|
370
313
|
)
|
|
371
314
|
.requiredOption('--issueUrl <url>', 'GitHub issue URL')
|
|
372
315
|
.option('--projectUrl <url>', 'GitHub project URL')
|
|
373
|
-
.option('--preparationStatus <status>', 'Status for issues in preparation')
|
|
374
|
-
.option(
|
|
375
|
-
'--awaitingWorkspaceStatus <status>',
|
|
376
|
-
'Status for issues awaiting workspace',
|
|
377
|
-
)
|
|
378
|
-
.option(
|
|
379
|
-
'--awaitingQualityCheckStatus <status>',
|
|
380
|
-
'Status for issues awaiting quality check',
|
|
381
|
-
)
|
|
382
316
|
.option(
|
|
383
317
|
'--thresholdForAutoReject <count>',
|
|
384
318
|
'Threshold for auto-escalation after consecutive rejections (default: 3)',
|
|
@@ -398,9 +332,6 @@ program
|
|
|
398
332
|
|
|
399
333
|
const cliOverrides: ConfigFile = {
|
|
400
334
|
projectUrl: options.projectUrl,
|
|
401
|
-
preparationStatus: options.preparationStatus,
|
|
402
|
-
awaitingWorkspaceStatus: options.awaitingWorkspaceStatus,
|
|
403
|
-
awaitingQualityCheckStatus: options.awaitingQualityCheckStatus,
|
|
404
335
|
thresholdForAutoReject: options.thresholdForAutoReject
|
|
405
336
|
? Number(options.thresholdForAutoReject)
|
|
406
337
|
: undefined,
|
|
@@ -426,9 +357,6 @@ program
|
|
|
426
357
|
);
|
|
427
358
|
|
|
428
359
|
const projectUrl = config.projectUrl;
|
|
429
|
-
const preparationStatus = config.preparationStatus;
|
|
430
|
-
const awaitingWorkspaceStatus = config.awaitingWorkspaceStatus;
|
|
431
|
-
const awaitingQualityCheckStatus = config.awaitingQualityCheckStatus;
|
|
432
360
|
|
|
433
361
|
if (!projectUrl) {
|
|
434
362
|
console.error(
|
|
@@ -436,24 +364,6 @@ program
|
|
|
436
364
|
);
|
|
437
365
|
process.exit(1);
|
|
438
366
|
}
|
|
439
|
-
if (!preparationStatus) {
|
|
440
|
-
console.error(
|
|
441
|
-
'preparationStatus is required. Provide via --preparationStatus, config file, or project README.',
|
|
442
|
-
);
|
|
443
|
-
process.exit(1);
|
|
444
|
-
}
|
|
445
|
-
if (!awaitingWorkspaceStatus) {
|
|
446
|
-
console.error(
|
|
447
|
-
'awaitingWorkspaceStatus is required. Provide via --awaitingWorkspaceStatus, config file, or project README.',
|
|
448
|
-
);
|
|
449
|
-
process.exit(1);
|
|
450
|
-
}
|
|
451
|
-
if (!awaitingQualityCheckStatus) {
|
|
452
|
-
console.error(
|
|
453
|
-
'awaitingQualityCheckStatus is required. Provide via --awaitingQualityCheckStatus, config file, or project README.',
|
|
454
|
-
);
|
|
455
|
-
process.exit(1);
|
|
456
|
-
}
|
|
457
367
|
|
|
458
368
|
let thresholdForAutoReject = 3;
|
|
459
369
|
const rawThreshold = config.thresholdForAutoReject;
|
|
@@ -487,16 +397,9 @@ program
|
|
|
487
397
|
cachePath,
|
|
488
398
|
token,
|
|
489
399
|
);
|
|
490
|
-
const projectRepository =
|
|
491
|
-
...
|
|
492
|
-
|
|
493
|
-
prepareStatus: async (
|
|
494
|
-
_name: string,
|
|
495
|
-
project: Project,
|
|
496
|
-
): Promise<Project> => {
|
|
497
|
-
return project;
|
|
498
|
-
},
|
|
499
|
-
};
|
|
400
|
+
const projectRepository = new GraphqlProjectRepository(
|
|
401
|
+
...githubRepositoryParams,
|
|
402
|
+
);
|
|
500
403
|
const apiV3IssueRepository = new ApiV3IssueRepository(
|
|
501
404
|
...githubRepositoryParams,
|
|
502
405
|
);
|
|
@@ -526,9 +429,6 @@ program
|
|
|
526
429
|
await useCase.run({
|
|
527
430
|
projectUrl,
|
|
528
431
|
issueUrl: options.issueUrl,
|
|
529
|
-
preparationStatus,
|
|
530
|
-
awaitingWorkspaceStatus,
|
|
531
|
-
awaitingQualityCheckStatus,
|
|
532
432
|
thresholdForAutoReject,
|
|
533
433
|
workflowBlockerResolvedWebhookUrl,
|
|
534
434
|
});
|