github-issue-tower-defence-management 1.40.0 → 1.42.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/.github/workflows/umino-project.yml +5 -4
- package/CHANGELOG.md +20 -0
- package/README.md +27 -9
- package/bin/adapter/entry-points/cli/index.js +68 -10
- package/bin/adapter/entry-points/cli/index.js.map +1 -1
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +44 -8
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/repositories/NodeLocalCommandRunner.js +3 -3
- package/bin/adapter/repositories/NodeLocalCommandRunner.js.map +1 -1
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +412 -177
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
- package/bin/domain/usecases/HandleScheduledEventUseCase.js +6 -2
- package/bin/domain/usecases/HandleScheduledEventUseCase.js.map +1 -1
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js +7 -2
- package/bin/domain/usecases/RevertOrphanedPreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/StartPreparationUseCase.js +115 -72
- package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/cli/index.test.ts +184 -13
- package/src/adapter/entry-points/cli/index.ts +105 -13
- package/src/adapter/repositories/NodeLocalCommandRunner.test.ts +12 -12
- package/src/adapter/repositories/NodeLocalCommandRunner.ts +7 -4
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.test.ts +3 -0
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +626 -265
- package/src/adapter/repositories/issue/RestIssueRepository.test.ts +3 -0
- package/src/domain/entities/Issue.ts +1 -0
- package/src/domain/usecases/GetStoryObjectMapUseCase.test.ts +1 -0
- package/src/domain/usecases/HandleScheduledEventUseCase.ts +13 -3
- package/src/domain/usecases/NotifyFinishedIssuePreparationUseCase.test.ts +1 -0
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +64 -9
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.ts +8 -3
- package/src/domain/usecases/StartPreparationUseCase.test.ts +1978 -295
- package/src/domain/usecases/StartPreparationUseCase.ts +185 -126
- package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +2 -1
- package/src/domain/usecases/adapter-interfaces/LocalCommandRunner.ts +4 -1
- package/types/adapter/entry-points/cli/index.d.ts +5 -1
- package/types/adapter/entry-points/cli/index.d.ts.map +1 -1
- package/types/adapter/repositories/NodeLocalCommandRunner.d.ts +1 -1
- package/types/adapter/repositories/NodeLocalCommandRunner.d.ts.map +1 -1
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts +5 -3
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
- package/types/domain/entities/Issue.d.ts +1 -0
- package/types/domain/entities/Issue.d.ts.map +1 -1
- package/types/domain/usecases/HandleScheduledEventUseCase.d.ts +6 -1
- package/types/domain/usecases/HandleScheduledEventUseCase.d.ts.map +1 -1
- package/types/domain/usecases/RevertOrphanedPreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/StartPreparationUseCase.d.ts +11 -18
- package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +2 -1
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/LocalCommandRunner.d.ts +1 -1
- package/types/domain/usecases/adapter-interfaces/LocalCommandRunner.d.ts.map +1 -1
|
@@ -55,6 +55,7 @@ describe('RestIssueRepository', () => {
|
|
|
55
55
|
isInProgress: false,
|
|
56
56
|
isClosed: false,
|
|
57
57
|
createdAt: new Date(),
|
|
58
|
+
author: '',
|
|
58
59
|
};
|
|
59
60
|
|
|
60
61
|
await restIssueRepository.updateLabels(issue, ['default']);
|
|
@@ -92,6 +93,7 @@ describe('RestIssueRepository', () => {
|
|
|
92
93
|
isInProgress: false,
|
|
93
94
|
isClosed: false,
|
|
94
95
|
createdAt: new Date(),
|
|
96
|
+
author: '',
|
|
95
97
|
};
|
|
96
98
|
|
|
97
99
|
await restIssueRepository.updateLabels(issue, ['test', 'to-remove']);
|
|
@@ -130,6 +132,7 @@ describe('RestIssueRepository', () => {
|
|
|
130
132
|
isInProgress: false,
|
|
131
133
|
isClosed: false,
|
|
132
134
|
createdAt: new Date(),
|
|
135
|
+
author: '',
|
|
133
136
|
};
|
|
134
137
|
await restIssueRepository.updateAssigneeList(issue, ['HiromiShikata']);
|
|
135
138
|
const issueWithAssignee = await restIssueRepository.getIssue(issue.url);
|
|
@@ -73,9 +73,14 @@ export class HandleScheduledEventUseCase {
|
|
|
73
73
|
awaitingWorkspaceStatus: string;
|
|
74
74
|
preparationStatus: string;
|
|
75
75
|
defaultAgentName: string;
|
|
76
|
-
|
|
76
|
+
defaultLlmModelName?: string | null;
|
|
77
|
+
defaultLlmAgentName?: string | null;
|
|
78
|
+
configFilePath: string;
|
|
77
79
|
maximumPreparingIssuesCount: number | null;
|
|
80
|
+
utilizationPercentageThreshold?: number;
|
|
81
|
+
allowedIssueAuthors?: string[] | null;
|
|
78
82
|
preparationProcessCheckCommand?: string;
|
|
83
|
+
codexHomeCandidates?: string[] | null;
|
|
79
84
|
} | null;
|
|
80
85
|
notifyFinishedPreparation?: {
|
|
81
86
|
preparationStatus: string;
|
|
@@ -344,10 +349,15 @@ ${JSON.stringify(e)}
|
|
|
344
349
|
awaitingWorkspaceStatus: input.startPreparation.awaitingWorkspaceStatus,
|
|
345
350
|
preparationStatus: input.startPreparation.preparationStatus,
|
|
346
351
|
defaultAgentName: input.startPreparation.defaultAgentName,
|
|
347
|
-
|
|
352
|
+
defaultLlmModelName: input.startPreparation.defaultLlmModelName ?? null,
|
|
353
|
+
defaultLlmAgentName: input.startPreparation.defaultLlmAgentName ?? null,
|
|
354
|
+
configFilePath: input.startPreparation.configFilePath,
|
|
348
355
|
maximumPreparingIssuesCount:
|
|
349
356
|
input.startPreparation.maximumPreparingIssuesCount,
|
|
350
|
-
|
|
357
|
+
utilizationPercentageThreshold:
|
|
358
|
+
input.startPreparation.utilizationPercentageThreshold ?? 90,
|
|
359
|
+
allowedIssueAuthors: input.startPreparation.allowedIssueAuthors ?? null,
|
|
360
|
+
codexHomeCandidates: input.startPreparation.codexHomeCandidates ?? null,
|
|
351
361
|
});
|
|
352
362
|
}
|
|
353
363
|
if (input.notifyFinishedPreparation) {
|
|
@@ -30,6 +30,7 @@ const createMockIssue = (overrides: Partial<Issue> = {}): Issue => ({
|
|
|
30
30
|
isInProgress: false,
|
|
31
31
|
isClosed: false,
|
|
32
32
|
createdAt: new Date(),
|
|
33
|
+
author: '',
|
|
33
34
|
...overrides,
|
|
34
35
|
});
|
|
35
36
|
|
|
@@ -136,9 +137,13 @@ describe('RevertOrphanedPreparationUseCase', () => {
|
|
|
136
137
|
expect(mockIssueRepository.createComment.mock.calls).toHaveLength(1);
|
|
137
138
|
expect(mockIssueRepository.createComment.mock.calls[0][0]).toBe(stuckIssue);
|
|
138
139
|
expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
|
|
139
|
-
expect(mockLocalCommandRunner.runCommand.mock.calls[0][0]).toBe(
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
expect(mockLocalCommandRunner.runCommand.mock.calls[0][0]).toBe('sh');
|
|
141
|
+
expect(mockLocalCommandRunner.runCommand.mock.calls[0][1]).toEqual([
|
|
142
|
+
'-c',
|
|
143
|
+
'pgrep -fa "claude-agent.*$1"',
|
|
144
|
+
'--',
|
|
145
|
+
'https://github.com/user/repo/issues/10',
|
|
146
|
+
]);
|
|
142
147
|
});
|
|
143
148
|
|
|
144
149
|
it('should leave in-flight Preparation issue untouched when check command exits zero', async () => {
|
|
@@ -196,9 +201,13 @@ describe('RevertOrphanedPreparationUseCase', () => {
|
|
|
196
201
|
});
|
|
197
202
|
|
|
198
203
|
expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
|
|
199
|
-
expect(mockLocalCommandRunner.runCommand.mock.calls[0][0]).toBe(
|
|
200
|
-
|
|
201
|
-
|
|
204
|
+
expect(mockLocalCommandRunner.runCommand.mock.calls[0][0]).toBe('sh');
|
|
205
|
+
expect(mockLocalCommandRunner.runCommand.mock.calls[0][1]).toEqual([
|
|
206
|
+
'-c',
|
|
207
|
+
'check $1',
|
|
208
|
+
'--',
|
|
209
|
+
'https://github.com/user/repo/issues/10',
|
|
210
|
+
]);
|
|
202
211
|
expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(1);
|
|
203
212
|
});
|
|
204
213
|
|
|
@@ -250,6 +259,48 @@ describe('RevertOrphanedPreparationUseCase', () => {
|
|
|
250
259
|
).rejects.toThrow('Project not found');
|
|
251
260
|
});
|
|
252
261
|
|
|
262
|
+
it('should throw when getProject returns null after findProjectIdByUrl succeeds', async () => {
|
|
263
|
+
mockProjectRepository.findProjectIdByUrl.mockResolvedValue('project-1');
|
|
264
|
+
mockProjectRepository.getProject.mockResolvedValue(null);
|
|
265
|
+
|
|
266
|
+
await expect(
|
|
267
|
+
useCase.run({
|
|
268
|
+
projectUrl: 'https://github.com/user/repo',
|
|
269
|
+
preparationStatus: 'Preparation',
|
|
270
|
+
awaitingWorkspaceStatus: 'Awaiting Workspace',
|
|
271
|
+
allowIssueCacheMinutes: 0,
|
|
272
|
+
preparationProcessCheckCommand: 'check {URL}',
|
|
273
|
+
}),
|
|
274
|
+
).rejects.toThrow('Project not found. projectId: project-1');
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
it('should do nothing when awaitingWorkspaceStatus is not found in project statuses', async () => {
|
|
278
|
+
const preparationIssue = createMockIssue({
|
|
279
|
+
url: 'https://github.com/user/repo/issues/10',
|
|
280
|
+
status: 'Preparation',
|
|
281
|
+
});
|
|
282
|
+
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
283
|
+
issues: [preparationIssue],
|
|
284
|
+
cacheUsed: false,
|
|
285
|
+
});
|
|
286
|
+
mockLocalCommandRunner.runCommand.mockResolvedValue({
|
|
287
|
+
stdout: '',
|
|
288
|
+
stderr: '',
|
|
289
|
+
exitCode: 1,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
await useCase.run({
|
|
293
|
+
projectUrl: 'https://github.com/user/repo',
|
|
294
|
+
preparationStatus: 'Preparation',
|
|
295
|
+
awaitingWorkspaceStatus: 'NonExistentStatus',
|
|
296
|
+
allowIssueCacheMinutes: 0,
|
|
297
|
+
preparationProcessCheckCommand: 'check {URL}',
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
expect(mockIssueRepository.updateStatus.mock.calls).toHaveLength(0);
|
|
301
|
+
expect(mockIssueRepository.createComment.mock.calls).toHaveLength(0);
|
|
302
|
+
});
|
|
303
|
+
|
|
253
304
|
it('should do nothing when there are no Preparation issues', async () => {
|
|
254
305
|
mockIssueRepository.getAllIssues.mockResolvedValue({
|
|
255
306
|
issues: [
|
|
@@ -295,8 +346,12 @@ describe('RevertOrphanedPreparationUseCase', () => {
|
|
|
295
346
|
});
|
|
296
347
|
|
|
297
348
|
expect(mockLocalCommandRunner.runCommand.mock.calls).toHaveLength(1);
|
|
298
|
-
expect(mockLocalCommandRunner.runCommand.mock.calls[0][0]).toBe(
|
|
299
|
-
|
|
300
|
-
|
|
349
|
+
expect(mockLocalCommandRunner.runCommand.mock.calls[0][0]).toBe('sh');
|
|
350
|
+
expect(mockLocalCommandRunner.runCommand.mock.calls[0][1]).toEqual([
|
|
351
|
+
'-c',
|
|
352
|
+
'pgrep -fa "claude-agent.*$1"',
|
|
353
|
+
'--',
|
|
354
|
+
'https://github.com/org/project/issues/99',
|
|
355
|
+
]);
|
|
301
356
|
});
|
|
302
357
|
});
|
|
@@ -51,11 +51,16 @@ export class RevertOrphanedPreparationUseCase {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
for (const issue of preparationIssues) {
|
|
54
|
-
const
|
|
54
|
+
const commandTemplate = params.preparationProcessCheckCommand.replace(
|
|
55
55
|
'{URL}',
|
|
56
|
-
|
|
56
|
+
'$1',
|
|
57
57
|
);
|
|
58
|
-
const { exitCode } = await this.localCommandRunner.runCommand(
|
|
58
|
+
const { exitCode } = await this.localCommandRunner.runCommand('sh', [
|
|
59
|
+
'-c',
|
|
60
|
+
commandTemplate,
|
|
61
|
+
'--',
|
|
62
|
+
issue.url,
|
|
63
|
+
]);
|
|
59
64
|
if (exitCode !== 0) {
|
|
60
65
|
await this.issueRepository.updateStatus(
|
|
61
66
|
project,
|